summaryrefslogtreecommitdiffstats
path: root/WebKitTools
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools')
-rw-r--r--WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json47
-rw-r--r--WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg28
-rw-r--r--WebKitTools/BuildSlaveSupport/build.webkit.org-config/public_html/index.html2
-rw-r--r--WebKitTools/BuildSlaveSupport/built-product-archive36
-rwxr-xr-xWebKitTools/BuildSlaveSupport/win/kill-old-processes37
-rw-r--r--WebKitTools/ChangeLog10304
-rw-r--r--WebKitTools/DumpRenderTree/AccessibilityController.cpp14
-rw-r--r--WebKitTools/DumpRenderTree/AccessibilityController.h1
-rw-r--r--WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp33
-rw-r--r--WebKitTools/DumpRenderTree/AccessibilityUIElement.h21
-rw-r--r--WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp186
-rw-r--r--WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj8
-rw-r--r--WebKitTools/DumpRenderTree/LayoutTestController.cpp166
-rw-r--r--WebKitTools/DumpRenderTree/LayoutTestController.h30
-rw-r--r--WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp144
-rw-r--r--WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h4
-rw-r--r--WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp38
-rw-r--r--WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp119
-rw-r--r--WebKitTools/DumpRenderTree/chromium/AccessibilityController.h73
-rw-r--r--WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp585
-rw-r--r--WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h142
-rw-r--r--WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp350
-rw-r--r--WebKitTools/DumpRenderTree/chromium/CppBoundClass.h241
-rw-r--r--WebKitTools/DumpRenderTree/chromium/CppVariant.cpp310
-rw-r--r--WebKitTools/DumpRenderTree/chromium/CppVariant.h133
-rw-r--r--WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp119
-rw-r--r--WebKitTools/DumpRenderTree/chromium/EventSender.cpp807
-rw-r--r--WebKitTools/DumpRenderTree/chromium/EventSender.h145
-rw-r--r--WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp407
-rw-r--r--WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp1179
-rw-r--r--WebKitTools/DumpRenderTree/chromium/LayoutTestController.h440
-rw-r--r--WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm118
-rw-r--r--WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp149
-rw-r--r--WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h82
-rw-r--r--WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp79
-rw-r--r--WebKitTools/DumpRenderTree/chromium/PlainTextController.h52
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp269
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestNavigationController.h204
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestShell.cpp614
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestShell.h149
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp64
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestShellMac.mm125
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp100
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TestWebWorker.h88
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TextInputController.cpp216
-rw-r--r--WebKitTools/DumpRenderTree/chromium/TextInputController.h149
-rw-r--r--WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp1316
-rw-r--r--WebKitTools/DumpRenderTree/chromium/WebViewHost.h271
-rw-r--r--WebKitTools/DumpRenderTree/chromium/config.h54
-rw-r--r--WebKitTools/DumpRenderTree/fonts/ColorBits-A.pngbin0 -> 585 bytes
-rw-r--r--WebKitTools/DumpRenderTree/fonts/ColorBits.ttfbin0 -> 1028 bytes
-rw-r--r--WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp6
-rw-r--r--WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp35
-rw-r--r--WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp128
-rw-r--r--WebKitTools/DumpRenderTree/gtk/EventSender.cpp44
-rw-r--r--WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp107
-rw-r--r--WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm6
-rw-r--r--WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm162
-rw-r--r--WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig32
-rw-r--r--WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig4
-rw-r--r--WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm69
-rw-r--r--WebKitTools/DumpRenderTree/mac/EventSendingController.mm105
-rw-r--r--WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm160
-rw-r--r--WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm18
-rw-r--r--WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro8
-rw-r--r--WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp113
-rw-r--r--WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h2
-rw-r--r--WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp36
-rw-r--r--WebKitTools/DumpRenderTree/qt/EventSenderQt.h10
-rw-r--r--WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp12
-rw-r--r--WebKitTools/DumpRenderTree/qt/GCControllerQt.h2
-rw-r--r--WebKitTools/DumpRenderTree/qt/ImageDiff.pro3
-rw-r--r--WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp203
-rw-r--r--WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h39
-rw-r--r--WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro1
-rw-r--r--WebKitTools/DumpRenderTree/qt/main.cpp7
-rw-r--r--WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp25
-rw-r--r--WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp6
-rw-r--r--WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp27
-rw-r--r--WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp38
-rw-r--r--WebKitTools/DumpRenderTree/win/EventSender.cpp25
-rw-r--r--WebKitTools/DumpRenderTree/win/EventSender.h2
-rw-r--r--WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp5
-rw-r--r--WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp130
-rw-r--r--WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp33
-rw-r--r--WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp29
-rw-r--r--WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp49
-rw-r--r--WebKitTools/EWSTools/boot.sh30
-rw-r--r--WebKitTools/EWSTools/create-webkit-git36
-rw-r--r--WebKitTools/EWSTools/screen-config4
-rw-r--r--WebKitTools/EWSTools/start-queue.sh37
-rw-r--r--WebKitTools/MiniBrowser/Makefile2
-rw-r--r--WebKitTools/MiniBrowser/MiniBrowser.vcproj271
-rw-r--r--WebKitTools/MiniBrowser/MiniBrowser.xcodeproj/project.pbxproj304
-rw-r--r--WebKitTools/MiniBrowser/mac/AppDelegate.h39
-rw-r--r--WebKitTools/MiniBrowser/mac/AppDelegate.m116
-rw-r--r--WebKitTools/MiniBrowser/mac/BrowserWindowController.h50
-rw-r--r--WebKitTools/MiniBrowser/mac/BrowserWindowController.m378
-rw-r--r--WebKitTools/MiniBrowser/mac/English.lproj/BrowserWindow.xib869
-rw-r--r--WebKitTools/MiniBrowser/mac/English.lproj/InfoPlist.strings2
-rw-r--r--WebKitTools/MiniBrowser/mac/English.lproj/MainMenu.xib4603
-rw-r--r--WebKitTools/MiniBrowser/mac/MiniBrowser-Info.plist32
-rw-r--r--WebKitTools/MiniBrowser/mac/MiniBrowser_Prefix.pch28
-rw-r--r--WebKitTools/MiniBrowser/mac/main.m31
-rw-r--r--WebKitTools/MiniBrowser/win/BrowserView.cpp104
-rw-r--r--WebKitTools/MiniBrowser/win/BrowserView.h49
-rw-r--r--WebKitTools/MiniBrowser/win/BrowserWindow.cpp245
-rw-r--r--WebKitTools/MiniBrowser/win/BrowserWindow.h70
-rw-r--r--WebKitTools/MiniBrowser/win/MiniBrowser.cpp84
-rw-r--r--WebKitTools/MiniBrowser/win/MiniBrowser.h55
-rw-r--r--WebKitTools/MiniBrowser/win/MiniBrowser.rc30
-rw-r--r--WebKitTools/MiniBrowser/win/main.cpp62
-rw-r--r--WebKitTools/MiniBrowser/win/resource.h22
-rw-r--r--WebKitTools/MiniBrowser/win/stdafx.cpp33
-rw-r--r--WebKitTools/MiniBrowser/win/stdafx.h30
-rw-r--r--WebKitTools/QtLauncher/QtLauncher.pro26
-rw-r--r--WebKitTools/QtLauncher/QtLauncher.qrc5
-rw-r--r--WebKitTools/QtLauncher/main.cpp655
-rw-r--r--WebKitTools/QtLauncher/mainwindow.cpp8
-rw-r--r--WebKitTools/QtLauncher/mainwindow.h3
-rw-r--r--WebKitTools/QtLauncher/useragentlist.txt9
-rw-r--r--WebKitTools/QtLauncher/utils.h12
-rw-r--r--WebKitTools/QtLauncher/webpage.cpp14
-rw-r--r--WebKitTools/QtLauncher/webpage.h7
-rw-r--r--WebKitTools/QtLauncher/webview.cpp37
-rw-r--r--WebKitTools/QtLauncher/webview.h12
-rw-r--r--WebKitTools/QueueStatusServer/handlers/__init__.py2
-rw-r--r--WebKitTools/QueueStatusServer/handlers/svnrevision.py40
-rw-r--r--WebKitTools/QueueStatusServer/handlers/updatebase.py41
-rw-r--r--WebKitTools/QueueStatusServer/handlers/updatestatus.py13
-rw-r--r--WebKitTools/QueueStatusServer/handlers/updatesvnrevision.py53
-rw-r--r--WebKitTools/QueueStatusServer/index.yaml6
-rw-r--r--WebKitTools/QueueStatusServer/main.py4
-rw-r--r--WebKitTools/QueueStatusServer/model/__init__.py2
-rw-r--r--WebKitTools/QueueStatusServer/model/queues.py6
-rw-r--r--WebKitTools/QueueStatusServer/model/svnrevision.py34
-rw-r--r--WebKitTools/QueueStatusServer/templates/dashboard.html12
-rw-r--r--WebKitTools/QueueStatusServer/templates/statusbubble.html12
-rw-r--r--WebKitTools/QueueStatusServer/templates/updatesvnrevision.html8
-rw-r--r--WebKitTools/Scripts/VCSUtils.pm71
-rwxr-xr-xWebKitTools/Scripts/build-dumprendertree12
-rwxr-xr-xWebKitTools/Scripts/build-webkit63
-rwxr-xr-xWebKitTools/Scripts/check-for-global-initializers24
-rwxr-xr-xWebKitTools/Scripts/check-for-inappropriate-files-in-framework66
-rwxr-xr-xWebKitTools/Scripts/check-for-webkit-framework-include-consistency109
-rwxr-xr-xWebKitTools/Scripts/check-webkit-style81
-rwxr-xr-xWebKitTools/Scripts/commit-log-editor39
-rwxr-xr-xWebKitTools/Scripts/debug-minibrowser38
-rwxr-xr-xWebKitTools/Scripts/new-run-webkit-httpd98
-rwxr-xr-x[-rw-r--r--]WebKitTools/Scripts/new-run-webkit-tests (renamed from WebKitTools/Scripts/webkitpy/steps/commit.py)18
-rw-r--r--WebKitTools/Scripts/new-run-webkit-websocketserver99
-rwxr-xr-xWebKitTools/Scripts/old-run-webkit-tests2281
-rwxr-xr-xWebKitTools/Scripts/prepare-ChangeLog44
-rwxr-xr-xWebKitTools/Scripts/rebaseline-chromium-webkit-tests9
-rwxr-xr-xWebKitTools/Scripts/resolve-ChangeLogs56
-rwxr-xr-xWebKitTools/Scripts/run-launcher4
-rwxr-xr-xWebKitTools/Scripts/run-minibrowser38
-rwxr-xr-xWebKitTools/Scripts/run-webkit-httpd4
-rwxr-xr-xWebKitTools/Scripts/run-webkit-tests2255
-rwxr-xr-xWebKitTools/Scripts/run-webkit-websocketserver15
-rwxr-xr-xWebKitTools/Scripts/svn-apply9
-rwxr-xr-xWebKitTools/Scripts/svn-create-patch44
-rwxr-xr-xWebKitTools/Scripts/test-webkitpy241
-rwxr-xr-xWebKitTools/Scripts/update-iexploder-cssproperties89
-rwxr-xr-xWebKitTools/Scripts/validate-committer-lists6
-rwxr-xr-xWebKitTools/Scripts/webkit-patch81
-rw-r--r--WebKitTools/Scripts/webkitdirs.pm119
-rw-r--r--WebKitTools/Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl336
-rw-r--r--WebKitTools/Scripts/webkitperl/httpd.pm1
-rw-r--r--WebKitTools/Scripts/webkitpy/__init__.py17
-rw-r--r--WebKitTools/Scripts/webkitpy/autoinstall.py335
-rw-r--r--WebKitTools/Scripts/webkitpy/buildbot.py133
-rw-r--r--WebKitTools/Scripts/webkitpy/buildbot_unittest.py155
-rw-r--r--WebKitTools/Scripts/webkitpy/commands/queries.py116
-rw-r--r--WebKitTools/Scripts/webkitpy/common/__init__.py (renamed from WebKitTools/Scripts/webkitpy/commands/__init__.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/api.py140
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py169
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/changelog.py (renamed from WebKitTools/Scripts/webkitpy/changelogs.py)73
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py (renamed from WebKitTools/Scripts/webkitpy/changelogs_unittest.py)31
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py95
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py61
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py (renamed from WebKitTools/Scripts/webkitpy/diff_parser.py)7
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py (renamed from WebKitTools/Scripts/webkitpy/diff_parser_unittest.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/scm.py (renamed from WebKitTools/Scripts/webkitpy/scm.py)287
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py (renamed from WebKitTools/Scripts/webkitpy/scm_unittest.py)300
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/__init__.py7
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers.py (renamed from WebKitTools/Scripts/webkitpy/committers.py)258
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py (renamed from WebKitTools/Scripts/webkitpy/committers_unittest.py)11
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/irc.py31
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/ports.py (renamed from WebKitTools/Scripts/webkitpy/webkitport.py)36
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py (renamed from WebKitTools/Scripts/webkitpy/webkitport_unittest.py)11
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla.py (renamed from WebKitTools/Scripts/webkitpy/bugzilla.py)109
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py (renamed from WebKitTools/Scripts/webkitpy/bugzilla_unittest.py)60
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/buildbot.py495
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py433
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/credentials.py (renamed from WebKitTools/Scripts/webkitpy/credentials.py)21
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py (renamed from WebKitTools/Scripts/webkitpy/credentials_unittest.py)18
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/networktransaction.py (renamed from WebKitTools/Scripts/webkitpy/networktransaction.py)13
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py (renamed from WebKitTools/Scripts/webkitpy/networktransaction_unittest.py)12
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/rietveld.py89
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/rietveld_unittest.py39
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/statusserver.py (renamed from WebKitTools/Scripts/webkitpy/statusserver.py)48
-rw-r--r--[-rwxr-xr-x]WebKitTools/Scripts/webkitpy/common/prettypatch.py (renamed from WebKitTools/Scripts/run-chromium-webkit-tests)36
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/__init__.py1
-rwxr-xr-xWebKitTools/Scripts/webkitpy/common/system/autoinstall.py518
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py (renamed from WebKitTools/Scripts/webkitpy/webkit_logging.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py (renamed from WebKitTools/Scripts/webkitpy/webkit_logging_unittest.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive.py (renamed from WebKitTools/Scripts/webkitpy/executive.py)68
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py (renamed from WebKitTools/Scripts/webkitpy/executive_unittest.py)3
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logtesting.py258
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logutils.py207
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py142
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/ospath.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/outputcapture.py (renamed from WebKitTools/Scripts/webkitpy/outputcapture.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user.py (renamed from WebKitTools/Scripts/webkitpy/user.py)23
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user_unittest.py (renamed from WebKitTools/Scripts/webkitpy/user_unittest.py)3
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/messagepump.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/util.py)48
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py53
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/driver_test.py24
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py (renamed from WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py)206
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py9
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py37
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py227
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py169
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py20
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_files.py21
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py35
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py22
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base.py155
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py239
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py111
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py82
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py60
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py189
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py87
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py91
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py46
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py7
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py400
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py99
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py223
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test.py25
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py448
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py110
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/win.py75
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py285
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py (renamed from WebKitTools/Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py)785
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py12
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py14
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/versioning.py133
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py134
-rw-r--r--WebKitTools/Scripts/webkitpy/steps/__init__.py56
-rw-r--r--WebKitTools/Scripts/webkitpy/steps/completerollout.py66
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checker.py474
-rwxr-xr-xWebKitTools/Scripts/webkitpy/style/checker_unittest.py340
-rw-r--r--WebKitTools/Scripts/webkitpy/style/error_handlers.py100
-rw-r--r--WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py134
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filereader.py148
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filereader_unittest.py151
-rw-r--r--WebKitTools/Scripts/webkitpy/style/main.py130
-rw-r--r--WebKitTools/Scripts/webkitpy/style/main_unittest.py124
-rw-r--r--WebKitTools/Scripts/webkitpy/style/optparser.py399
-rw-r--r--WebKitTools/Scripts/webkitpy/style/optparser_unittest.py216
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/common.py36
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py60
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/cpp.py39
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/cpp_unittest.py102
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/python.py56
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/python_unittest.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/style/processors/python_unittest_input.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/style/unittests.py44
-rw-r--r--WebKitTools/Scripts/webkitpy/style_references.py29
-rw-r--r--WebKitTools/Scripts/webkitpy/test/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/test/main.py129
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py (renamed from WebKitTools/Scripts/webkitpy/BeautifulSoup.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/__init__.py97
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/mock.py (renamed from WebKitTools/Scripts/webkitpy/mock.py)618
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING (renamed from WebKitTools/pywebsocket/COPYING)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in (renamed from WebKitTools/pywebsocket/MANIFEST.in)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README (renamed from WebKitTools/pywebsocket/README)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit14
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py (renamed from WebKitTools/pywebsocket/example/echo_client.py)10
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py (renamed from WebKitTools/pywebsocket/example/echo_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt11
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/__init__.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/handshake.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/memorizingfile.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py (renamed from WebKitTools/pywebsocket/mod_pywebsocket/standalone.py)62
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py121
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py (renamed from WebKitTools/pywebsocket/setup.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py (renamed from WebKitTools/pywebsocket/test/config.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py (renamed from WebKitTools/pywebsocket/test/mock.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py (renamed from WebKitTools/pywebsocket/test/run_all.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py (renamed from WebKitTools/pywebsocket/test/test_dispatch.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py (renamed from WebKitTools/pywebsocket/test/test_handshake.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py (renamed from WebKitTools/pywebsocket/test/test_memorizingfile.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py (renamed from WebKitTools/pywebsocket/test/test_mock.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py (renamed from WebKitTools/pywebsocket/test/test_msgutil.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py (renamed from WebKitTools/pywebsocket/test/test_util.py)15
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README1
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py (renamed from WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl2
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt (renamed from WebKitTools/simplejson/LICENSE.txt)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt (renamed from WebKitTools/simplejson/README.txt)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py (renamed from WebKitTools/simplejson/__init__.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c (renamed from WebKitTools/simplejson/_speedups.c)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py (renamed from WebKitTools/simplejson/decoder.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py (renamed from WebKitTools/simplejson/encoder.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py (renamed from WebKitTools/simplejson/jsonfilter.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py (renamed from WebKitTools/simplejson/scanner.py)0
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py82
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/patchcollection.py (renamed from WebKitTools/Scripts/webkitpy/patchcollection.py)3
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/patchcollection_unittest.py (renamed from WebKitTools/Scripts/webkitpy/patchcollection_unittest.py)7
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py (renamed from WebKitTools/Scripts/webkitpy/queueengine.py)38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py (renamed from WebKitTools/Scripts/webkitpy/queueengine_unittest.py)17
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py131
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py93
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/__init__.py4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py (renamed from WebKitTools/Scripts/webkitpy/commands/abstractsequencedcommand.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py (renamed from WebKitTools/Scripts/webkitpy/commands/commandtest.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download.py (renamed from WebKitTools/Scripts/webkitpy/commands/download.py)147
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/download_unittest.py)40
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py (renamed from WebKitTools/Scripts/webkitpy/commands/early_warning_system.py)82
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/early_warning_system_unittest.py)39
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py (renamed from WebKitTools/Scripts/webkitpy/commands/openbugs.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/openbugs_unittest.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries.py285
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/queries_unittest.py)10
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues.py (renamed from WebKitTools/Scripts/webkitpy/commands/queues.py)195
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/queues_unittest.py)82
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py (renamed from WebKitTools/Scripts/webkitpy/commands/queuestest.py)17
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py107
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py47
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py (renamed from WebKitTools/Scripts/webkitpy/stepsequence.py)11
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload.py (renamed from WebKitTools/Scripts/webkitpy/commands/upload.py)42
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py (renamed from WebKitTools/Scripts/webkitpy/commands/upload_unittest.py)64
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/comments.py (renamed from WebKitTools/Scripts/webkitpy/comments.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/grammar.py (renamed from WebKitTools/Scripts/webkitpy/grammar.py)7
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py (renamed from WebKitTools/Scripts/webkitpy/grammar_unittest.py)5
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/main.py139
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool.py (renamed from WebKitTools/Scripts/webkitpy/mock_bugzillatool.py)219
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/multicommandtool.py (renamed from WebKitTools/Scripts/webkitpy/multicommandtool.py)13
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py (renamed from WebKitTools/Scripts/webkitpy/multicommandtool_unittest.py)10
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/__init__.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py (renamed from WebKitTools/Scripts/webkitpy/steps/abstractstep.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py (renamed from WebKitTools/Scripts/webkitpy/steps/applypatch.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py (renamed from WebKitTools/Scripts/webkitpy/steps/applypatchwithlocalcommit.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/build.py (renamed from WebKitTools/Scripts/webkitpy/steps/build.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py (renamed from WebKitTools/Scripts/webkitpy/steps/checkstyle.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py (renamed from WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectory.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py (renamed from WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectorywithlocalcommits.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebug.py (renamed from WebKitTools/Scripts/webkitpy/steps/closebug.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py (renamed from WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py (renamed from WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff_unittest.py)10
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py (renamed from WebKitTools/Scripts/webkitpy/steps/closepatch.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/commit.py37
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/createbug.py (renamed from WebKitTools/Scripts/webkitpy/steps/createbug.py)9
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py (renamed from WebKitTools/Scripts/webkitpy/steps/editchangelog.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py (renamed from WebKitTools/Scripts/webkitpy/steps/ensurebuildersaregreen.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py (renamed from WebKitTools/Scripts/webkitpy/steps/ensurelocalcommitifneeded.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/metastep.py (renamed from WebKitTools/Scripts/webkitpy/steps/metastep.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py (renamed from WebKitTools/Scripts/webkitpy/steps/obsoletepatches.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/options.py (renamed from WebKitTools/Scripts/webkitpy/steps/options.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py (renamed from WebKitTools/Scripts/webkitpy/steps/postdiff.py)12
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py (renamed from WebKitTools/Scripts/webkitpy/steps/postdiffforcommit.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py (renamed from WebKitTools/Scripts/webkitpy/steps/preparechangelog.py)8
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py (renamed from WebKitTools/Scripts/webkitpy/steps/preparechangelogforrevert.py)11
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py (renamed from WebKitTools/Scripts/webkitpy/steps/promptforbugortitle.py)2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py (renamed from WebKitTools/Scripts/webkitpy/steps/confirmdiff.py)27
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py (renamed from WebKitTools/Scripts/webkitpy/steps/revertrevision.py)4
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/runtests.py (renamed from WebKitTools/Scripts/webkitpy/steps/runtests.py)9
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py (renamed from WebKitTools/Scripts/webkitpy/steps/steps_unittest.py)14
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/update.py (renamed from WebKitTools/Scripts/webkitpy/steps/update.py)6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py (renamed from WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreview_unittests.py)12
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py (renamed from WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreviewer.py)12
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py64
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py58
-rw-r--r--WebKitTools/TestResultServer/app.yaml19
-rw-r--r--WebKitTools/TestResultServer/handlers/__init__.py1
-rw-r--r--WebKitTools/TestResultServer/handlers/dashboardhandler.py120
-rw-r--r--WebKitTools/TestResultServer/handlers/menu.py64
-rw-r--r--WebKitTools/TestResultServer/handlers/testfilehandler.py221
-rw-r--r--WebKitTools/TestResultServer/index.yaml50
-rw-r--r--WebKitTools/TestResultServer/main.py60
-rw-r--r--WebKitTools/TestResultServer/model/__init__.py1
-rw-r--r--WebKitTools/TestResultServer/model/dashboardfile.py116
-rw-r--r--WebKitTools/TestResultServer/model/testfile.py122
-rw-r--r--WebKitTools/TestResultServer/stylesheets/dashboardfile.css30
-rw-r--r--WebKitTools/TestResultServer/stylesheets/form.css26
-rw-r--r--WebKitTools/TestResultServer/stylesheets/menu.css28
-rw-r--r--WebKitTools/TestResultServer/stylesheets/testfile.css30
-rw-r--r--WebKitTools/TestResultServer/templates/dashboardfilelist.html38
-rw-r--r--WebKitTools/TestResultServer/templates/menu.html27
-rw-r--r--WebKitTools/TestResultServer/templates/showfilelist.html53
-rw-r--r--WebKitTools/TestResultServer/templates/uploadform.html26
-rw-r--r--WebKitTools/WinLauncher/WinLauncher.h230
-rw-r--r--WebKitTools/gdb/webcore.py195
-rw-r--r--WebKitTools/iExploder/htdocs/cssproperties.in3
-rw-r--r--WebKitTools/iExploder/htdocs/htmlattrs.in10
-rw-r--r--WebKitTools/iExploder/htdocs/htmltags.in2
-rw-r--r--WebKitTools/wx/browser/browser.cpp2
-rw-r--r--WebKitTools/wx/build/settings.py14
436 files changed, 48159 insertions, 7237 deletions
diff --git a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json
index 3477c97..db7379d 100644
--- a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json
+++ b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json
@@ -15,6 +15,7 @@
{ "name": "apple-macpro-1", "platform": "mac-snowleopard" },
{ "name": "apple-macpro-2", "platform": "mac-snowleopard" },
+ { "name": "apple-macpro-3", "platform": "mac-snowleopard" },
{ "name": "apple-windows-1", "platform": "win"},
{ "name": "apple-windows-2", "platform": "win"},
@@ -27,10 +28,17 @@
{ "name": "gtk-linux-slave-4", "platform": "gtk"},
{ "name": "szeged-linux-1", "platform": "qt"},
+ { "name": "szeged-linux-2", "platform": "qt"},
+ { "name": "szeged-linux-3", "platform": "qt"},
+ { "name": "szeged-linux-4", "platform": "qt"},
+
+ { "name": "szeged-windows-1", "platform": "qt"},
+ { "name": "szeged-windows-2", "platform": "qt"},
{ "name": "google-windows-1", "platform": "chromium" },
{ "name": "google-mac-1", "platform": "chromium" },
- { "name": "google-linux-1", "platform": "chromium" }
+ { "name": "google-linux-1", "platform": "chromium" },
+ { "name": "google-new-tests", "platform": "mac-leopard" }
],
"builders": [ { "name": "Tiger Intel Release", "type": "BuildAndTest", "builddir": "tiger-intel-release",
@@ -68,7 +76,7 @@
},
{ "name": "SnowLeopard Intel Leaks", "type": "BuildAndTestLeaks", "builddir": "snowleopard-intel-leaks",
"platform": "mac-snowleopard", "configuration": "debug", "architectures": ["x86_64"],
- "slavenames": ["apple-macpro-1", "test-slave"]
+ "slavenames": ["apple-macpro-1", "apple-macpro-3", "test-slave"]
},
{
"name": "Windows Release (Build)", "type": "Build", "builddir": "win-release",
@@ -118,6 +126,31 @@
"slavenames": ["szeged-linux-1"]
},
{
+ "name": "Qt Linux Release minimal", "type": "Build", "builddir": "qt-linux-release-minimal",
+ "platform": "qt", "configuration": "release", "architectures": ["i386"],
+ "slavenames": ["szeged-linux-2"]
+ },
+ {
+ "name": "Qt Linux ARMv5 Release", "type": "Build", "builddir": "qt-linux-armv5-release",
+ "platform": "qt", "configuration": "release", "architectures": ["armv5"],
+ "slavenames": ["szeged-linux-3"]
+ },
+ {
+ "name": "Qt Linux ARMv7 Release", "type": "Build", "builddir": "qt-linux-armv7-release",
+ "platform": "qt", "configuration": "release", "architectures": ["armv7"],
+ "slavenames": ["szeged-linux-4"]
+ },
+ {
+ "name": "Qt Windows 32-bit Release", "type": "Build", "builddir": "qt-windows-32bit-release",
+ "platform": "qt", "configuration": "release", "architectures": ["i386"],
+ "slavenames": ["szeged-windows-1"]
+ },
+ {
+ "name": "Qt Windows 32-bit Debug", "type": "Build", "builddir": "qt-windows-32bit-debug",
+ "platform": "qt", "configuration": "debug", "architectures": ["i386"],
+ "slavenames": ["szeged-windows-2"]
+ },
+ {
"name": "Chromium Win Release", "type": "Build", "builddir": "chromium-win-release",
"platform": "chromium", "configuration": "release", "architectures": ["i386"],
"slavenames": ["google-windows-1"]
@@ -131,7 +164,12 @@
"name": "Chromium Linux Release", "type": "Build", "builddir": "chromium-linux-release",
"platform": "chromium", "configuration": "release", "architectures": ["i386"],
"slavenames": ["google-linux-1"]
- }
+ },
+ {
+ "name": "New run-webkit-tests", "type": "NewBuildAndTest", "builddir": "google-new-tests",
+ "platform": "mac-leopard", "configuration": "release", "architectures": ["i386"],
+ "slavenames": ["google-new-tests"]
+ }
],
"schedulers": [ { "type": "AnyBranchScheduler", "name": "trunk", "branches": ["trunk"], "treeStableTimer": 45.0,
@@ -139,7 +177,8 @@
"SnowLeopard Intel Release (Build)", "SnowLeopard Intel Leaks",
"Windows Release (Build)", "Windows Debug (Build)",
"GTK Linux 32-bit Release", "GTK Linux 32-bit Debug", "GTK Linux 64-bit Debug", "GTK Linux 64-bit Release",
- "Qt Linux Release",
+ "Qt Linux Release", "Qt Linux Release minimal", "Qt Linux ARMv5 Release", "Qt Linux ARMv7 Release",
+ "Qt Windows 32-bit Release", "Qt Windows 32-bit Debug",
"Chromium Win Release", "Chromium Mac Release", "Chromium Linux Release"]
},
{ "type": "Triggerable", "name": "leopard-intel-release-tests",
diff --git a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg
index 1823277..cd5e4e1 100644
--- a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg
+++ b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg
@@ -16,6 +16,8 @@ from twisted.internet import defer
import re
import simplejson
+from webkitpy.common.net.buildbot import BuildBot as wkbuildbot
+
WithProperties = properties.WithProperties
class ConfigureBuild(buildstep.BuildStep):
@@ -53,6 +55,11 @@ class InstallWin32Dependencies(shell.Compile):
descriptionDone = ["installed dependencies"]
command = ["perl", "./WebKitTools/Scripts/update-webkit-auxiliary-libs"]
+class KillOldProcesses(shell.Compile):
+ name = "kill old processes"
+ description = ["killing old processes"]
+ descriptionDone = ["killed old processes"]
+ command = ["python", "./WebKitTools/BuildSlaveSupport/win/kill-old-processes"]
class InstallChromiumDependencies(shell.ShellCommand):
name = "gclient"
@@ -230,6 +237,12 @@ class RunWebKitTests(shell.Test):
return [self.name]
+class NewRunWebKitTests(RunWebKitTests):
+ command = ["./WebKitTools/Scripts/new-run-webkit-tests", "--noshow-results",
+ "--results-directory", "layout-test-results",
+ WithProperties("--%(configuration)s")]
+
+
class RunGtkAPITests(shell.Test):
name = "API tests"
description = ["API tests running"]
@@ -261,7 +274,7 @@ class RunGtkAPITests(shell.Test):
def getText2(self, cmd, results):
if results != SUCCESS and self.incorrectLines:
- return ["%d API tests failed" % self.incorrectLines]
+ return ["%d API tests failed" % len(self.incorrectLines)]
return [self.name]
@@ -301,7 +314,7 @@ class ExtractTestResults(master.MasterShellCommand):
return master.MasterShellCommand.start(self)
def finished(self, result):
- url = self.build.getProperties().render(self.resultDirectory).replace("public_html/", "")
+ url = self.build.getProperties().render(self.resultDirectory).replace("public_html/", "/")
self.addURL("view results", url)
result = master.MasterShellCommand.finished(self, result)
self.step_status.setText(["uploaded results"])
@@ -314,6 +327,7 @@ class Factory(factory.BuildFactory):
self.addStep(ConfigureBuild, platform=platform, configuration=configuration, architecture=" ".join(architectures), buildOnly=buildOnly)
self.addStep(CheckOutSource)
if platform == "win":
+ self.addStep(KillOldProcesses)
self.addStep(InstallWin32Dependencies)
if platform == "chromium":
self.addStep(InstallChromiumDependencies)
@@ -354,12 +368,18 @@ class BuildAndTestFactory(Factory):
class BuildAndTestLeaksFactory(BuildAndTestFactory):
TestClass = RunWebKitLeakTests
+class NewBuildAndTestFactory(BuildAndTestFactory):
+ TestClass = NewRunWebKitTests
+
def loadBuilderConfig(c):
passwords = simplejson.load(open('passwords.json'))
config = simplejson.load(open('config.json'))
+ # use webkitpy's buildbot module to test for core builders
+ wkbb = wkbuildbot()
+
c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_builds=1) for slave in config['slaves']]
c['schedulers'] = []
@@ -388,6 +408,10 @@ def loadBuilderConfig(c):
builder["factory"] = factory(*factoryArgs)
+ builder["category"] = "noncore"
+ if wkbb._is_core_builder(builder['name']):
+ builder["category"] = "core"
+
c['builders'].append(builder)
loadBuilderConfig(c)
diff --git a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/public_html/index.html b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/public_html/index.html
index b38adc9..d819cc7 100644
--- a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/public_html/index.html
+++ b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/public_html/index.html
@@ -9,6 +9,8 @@
<h1>Welcome to the Buildbot!</h1>
<ul>
+ <li><a href="console?category=core">Core Console</a></li>
+ <li><a href="waterfall?category=core">Core Waterfall</a></li>
<li><a href="console">Console</a></li>
<li><a href="waterfall">Waterfall Display</a> will give you a time-oriented summary of recent buildbot activity.</li>
<li><a href="one_box_per_builder">Latest Build</a> for each builder is here.</li>
diff --git a/WebKitTools/BuildSlaveSupport/built-product-archive b/WebKitTools/BuildSlaveSupport/built-product-archive
index ca43dad..b27cf77 100644
--- a/WebKitTools/BuildSlaveSupport/built-product-archive
+++ b/WebKitTools/BuildSlaveSupport/built-product-archive
@@ -48,7 +48,7 @@ def main():
def archiveBuiltProduct(configuration, platform):
- assert platform in ('mac', 'win')
+ assert platform in ('mac', 'win','qt')
archiveFile = os.path.join(buildDirectory, configuration + ".zip")
@@ -81,8 +81,30 @@ def archiveBuiltProduct(configuration, platform):
shutil.rmtree(thinDirectory)
+ elif platform == 'qt':
+ configurationBuildDirectory = os.path.join(buildDirectory, configuration.title())
+ thinDirectory = os.path.join(configurationBuildDirectory, "thin")
+
+ if os.path.isdir(thinDirectory):
+ shutil.rmtree(thinDirectory)
+ os.mkdir(thinDirectory)
+
+ for dirname in ["bin", "lib", "JavaScriptCore"]:
+ fromDir = os.path.join(configurationBuildDirectory, dirname)
+ toDir = os.path.join(thinDirectory, dirname)
+ if subprocess.call(["cp", "-R", fromDir, toDir]):
+ return 1
+
+ for root, dirs, files in os.walk(thinDirectory, topdown=False):
+ for name in files:
+ if name.endswith(".o"):
+ os.remove(os.path.join(root, name))
+
+ if subprocess.call(["zip", "-y", "-r", archiveFile, "."], cwd=thinDirectory):
+ return 1
+
def extractBuiltProduct(configuration, platform):
- assert platform in ('mac', 'win')
+ assert platform in ('mac', 'win','qt')
archiveFile = os.path.join(buildDirectory, configuration + ".zip")
@@ -112,7 +134,15 @@ def extractBuiltProduct(configuration, platform):
if subprocess.call(["unzip", "-o", archiveFile], cwd=buildDirectory):
return 1
-
+ elif platform == 'qt':
+ configurationBuildDirectory = os.path.join(buildDirectory, configuration.title())
+
+ if os.path.isdir(configurationBuildDirectory):
+ shutil.rmtree(configurationBuildDirectory)
+
+ if subprocess.call(["unzip", "-o", archiveFile, "-d", configurationBuildDirectory], cwd=buildDirectory):
+ return 1
+ os.unlink(archiveFile)
if __name__ == '__main__':
sys.exit(main())
diff --git a/WebKitTools/BuildSlaveSupport/win/kill-old-processes b/WebKitTools/BuildSlaveSupport/win/kill-old-processes
new file mode 100755
index 0000000..50fb8a5
--- /dev/null
+++ b/WebKitTools/BuildSlaveSupport/win/kill-old-processes
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+# Copyright (C) 2010 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 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.
+
+import os, sys
+
+def main():
+ tasksToKill = ["DumpRenderTree.exe", "DumpRenderTree_debug.exe", "testapi.exe", "testapi_debug.exe",
+ "svn.exe", "httpd.exe", "cl.exe", "link.exe", "midl.exe", "devenv.exe", "perl.exe",
+ "imagediff.exe", "imagediff_debug.exe", "jsc.exe", "jsc_debug.exe"]
+
+ for task in tasksToKill:
+ os.system("taskkill /f /im " + task)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog
index 6a4b7b3..a03ae52 100644
--- a/WebKitTools/ChangeLog
+++ b/WebKitTools/ChangeLog
@@ -1,3 +1,10307 @@
+2010-04-21 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Alexey Proskuryakov.
+
+ aria-liveregion-notifications.html fails on leopard release bot
+ https://bugs.webkit.org/show_bug.cgi?id=37112
+
+ Change the way that notifications are listened for by forcing clients
+ to call a remove listener as well to match the add listener. DRT will
+ assert if those are not done in the correct order.
+
+ * DumpRenderTree/AccessibilityUIElement.cpp:
+ (removeNotificationListenerCallback):
+ (AccessibilityUIElement::getJSClass):
+ * DumpRenderTree/AccessibilityUIElement.h:
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::removeNotificationListener):
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+ (-[AccessibilityNotificationHandler initWithPlatformElement:]):
+ (-[AccessibilityNotificationHandler dealloc]):
+ (-[AccessibilityNotificationHandler _notificationReceived:]):
+ (-[AccessibilityNotificationHandler setCallback:]):
+ (AccessibilityUIElement::AccessibilityUIElement):
+ (AccessibilityUIElement::~AccessibilityUIElement):
+ (AccessibilityUIElement::addNotificationListener):
+ (AccessibilityUIElement::removeNotificationListener):
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::removeNotificationListener):
+
+2010-04-21 Anders Carlsson <andersca@apple.com>
+
+ Reviewed by Sam Weinig.
+
+ Add debug menu items to show/hide the Web View.
+ https://bugs.webkit.org/show_bug.cgi?id=37958
+
+ * MiniBrowser/mac/BrowserWindowController.h:
+ * MiniBrowser/mac/BrowserWindowController.m:
+ (-[BrowserWindowController showHideWebView:]):
+ (-[BrowserWindowController removeReinsertWebView:]):
+ (-[BrowserWindowController validateMenuItem:]):
+ * MiniBrowser/mac/English.lproj/MainMenu.xib:
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Unreviewed. Rolling out unicode() changes as they broke NRWT for chromium.
+ Rolling out:
+ http://trac.webkit.org/changeset/58014
+ http://trac.webkit.org/changeset/58016
+ http://trac.webkit.org/changeset/58020
+
+ REGRESSION(57531): the commit-queue still hates Tor Arne Vestbo
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ * Scripts/webkitpy/common/config/committers.py:
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+ * Scripts/webkitpy/common/net/statusserver.py:
+ * Scripts/webkitpy/common/prettypatch.py:
+ * Scripts/webkitpy/common/system/autoinstall.py:
+ * Scripts/webkitpy/common/system/deprecated_logging.py:
+ * Scripts/webkitpy/common/system/executive.py:
+ * Scripts/webkitpy/common/system/executive_unittest.py:
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ * Scripts/webkitpy/layout_tests/port/apache_http_server.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/gtk.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/mac_unittest.py:
+ * Scripts/webkitpy/layout_tests/port/qt.py:
+ * Scripts/webkitpy/layout_tests/port/server_process.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py:
+ * Scripts/webkitpy/style/filereader_unittest.py:
+ * Scripts/webkitpy/thirdparty/__init__.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+ * Scripts/webkitpy/tool/steps/postdiffforcommit.py:
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, fixing NRWT for real this time.
+
+ REGRESSION(57531): the commit-queue still hates Tor Arne Vestbø
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ - Add a hack to fix new-run-webkit-tests
+ my understanding of codecs.open(encoding=None)
+ must have been wrong.
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, just fixing exception seen on builders.
+
+ REGRESSION(57531): the commit-queue still hates Tor Arne Vestbø
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ - Pass and encoding to _write_into_file_at_path
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ REGRESSION(57531): the commit-queue still hates Tor Arne Vestbø
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ I fixed the queue to not ignore Tor as a reviwer in r57531,
+ but instead it throws an exception every time his name is in a patch.
+
+ This fixes our Executive.run_command code to work around a Popen
+ bug http://bugs.python.org/issue5290 whereby python versions before 2.6
+ do not correctly handle unicode objects as input or output to
+ Popen.communicate.
+
+ Following the advice of:
+ http://farmdev.com/talks/unicode/
+ I have changed all of webkitpy to use unicode() objects as strings
+ instead of str objects (which in Python 3 are renamed "bytes").
+
+ String literals were left as "foo" instead of converting to u"foo"
+ as u"foo" is only required if the string has a non-ascii code point.
+ Python is smart about comparing str() and unicode() values and will
+ log an error to the console if the comparison is ever invalid.
+
+ Executive.run* now correctly accept and return unicode() objects.
+ I attempted to fix all the places that we call .write() to make sure we
+ encode any unicode() objects into utf-8.
+
+ I removed several uses of StringIO. StringIO should generally always be
+ passed a unicode() value.
+
+ Likewise I replaced most calls to open() with codecs.open().
+ codecs.open() matches Python 3 open semantics in requiring an encoding
+ parameter. Files opened with codecs.open() with a unicode-compatible
+ encoding will vend unicode() objects from their read() calls, like how
+ StringIO created with a unicode() object will do.
+
+ I also deployed "with" statements wider (now that the project has
+ settled on Python 2.5) to close a bunch of file descriptor leaks.
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ - Read/write utf-8 files instead of ascii.
+ - Update the tests to use test for proper unicode() handling.
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ - Document that parse_latest_entry_from_file expects
+ file-like objects which return unicode strings.
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+ - Use unicode() strings instead of str() byte arrays.
+ - Deploy "with" to close file descriptor leaks.
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ - Remove unneeded import.
+ * Scripts/webkitpy/common/checkout/scm.py:
+ - Remove use of str().
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ - Read/write utf-8 files and use unicode() strings in testing.
+ * Scripts/webkitpy/common/config/committers.py:
+ - Use \u instead of \x to make slightly clearer what we're doing.
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ - Add a new _string_contents() method and explain why
+ we have to call unicode() on the result of soup.string
+ and why it's safe to do so w/o needing to pass a codec name.
+ - Remove the (unused) support for passing a file object to add_patch_to_bug().
+ * Scripts/webkitpy/common/net/buildbot.py:
+ - Use unicode() instead of str() when needing to coax a
+ NavigableString object into a unicode() object.
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+ - Add a test which contains a unicode builder name.
+ * Scripts/webkitpy/common/net/statusserver.py:
+ - Remove use of str()
+ * Scripts/webkitpy/common/prettypatch.py:
+ - Write out the patch file as utf-8.
+ * Scripts/webkitpy/common/system/autoinstall.py:
+ - Write out files with a explicit encodings.
+ - Deploy "with" to close file descriptor leaks.
+ * Scripts/webkitpy/common/system/deprecated_logging.py:
+ - Write out log files as utf-8.
+ * Scripts/webkitpy/common/system/executive.py:
+ - Make run* properly take and return unicode() objects.
+ - Cleaned up input handling in run_command a little by adding
+ a _compute_input() method which can return early instead of having
+ such a long/cluttered if-block.
+ * Scripts/webkitpy/common/system/executive_unittest.py:
+ - Added a unit test to make sure we don't break Tor again!
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ - Write out the test list as utf-8.
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ - Write out json files as utf-8.
+ * Scripts/webkitpy/layout_tests/port/apache_http_server.py:
+ - Deploy "with" to close file descriptor leaks.
+ * Scripts/webkitpy/layout_tests/port/chromium.py: Add Executive.py FIXME.
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py: ditto.
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py: ditto.
+ * Scripts/webkitpy/layout_tests/port/gtk.py: ditto.
+ * Scripts/webkitpy/layout_tests/port/mac.py: ditto.
+ * Scripts/webkitpy/layout_tests/port/mac_unittest.py:
+ - Make the skipped file parsing test unicode.
+ * Scripts/webkitpy/layout_tests/port/qt.py: Add Executive.py FIXME.
+ * Scripts/webkitpy/layout_tests/port/server_process.py: ditto.
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ - Deploy "with" to close file descriptor leaks.
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ - Make explicit the encodings of log files and pid files.
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ - Make encodings explicit and deploy "with".
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py: ditto.
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py: ditto.
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py: ditto.
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py: ditto.
+ * Scripts/webkitpy/style/filereader_unittest.py: ditto.
+ * Scripts/webkitpy/thirdparty/__init__.py: ditto.
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ - Removed extra import.
+ * Scripts/webkitpy/tool/commands/queues.py:
+ - No need to map args to strings now that run_command does.
+ - Update test results to match args changes.
+ - Document our global argument hacks.
+ * Scripts/webkitpy/tool/commands/upload.py:
+ - Pass the diff directly to add_patch_to_bug instead of creating a StringIO file wrapper.
+ * Scripts/webkitpy/tool/mocktool.py:
+ - Rename add_patch_to_bug argument to match bugzilla.py
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ - Executive.run_* now require lists instead of strings.
+ The lack of this change was what broke webkit-patch
+ for svn users the first time this was landed.
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+ - Pass the diff directly to add_patch_to_bug instead of creating a StringIO file wrapper.
+ * Scripts/webkitpy/tool/steps/postdiffforcommit.py: ditto
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py: ditto
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ - Fixed spurious logging seen when running test-webkitpy
+
+2010-04-21 Kinuko Yasuda <kinuko@chromium.org>
+
+ Unreviewed.
+
+ Add myself in committers.py.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-21 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ new-run-webkit-tests: fix a bug in the Chromium port where we would
+ try to talk to a crashed test_shell and raise exceptions that weren't
+ being caught.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37941
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-04-21 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Unreviewed.
+
+ [Qt] [Symbian] Build fix.
+
+ Work around a Qt quirk. Some versions of Symbian port Qt
+ QFontDatabase::removeAllApplicationFonts symbol is not available.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::open):
+
+2010-04-21 Alexey Proskuryakov <ap@apple.com>
+
+ Unreviewed.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37933
+ <rdar://problem/7719540> XMLHttpRequest.withCredentials should be better enforced.
+
+ Adding stub implementation of authenticateSession(). Depending on platform loader behavior,
+ a real implementation may or may not be necessary for the one test that currently uses it
+ to pass.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::authenticateSession):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-21 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Brady Eidson.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37933
+ <rdar://problem/7719540> XMLHttpRequest.withCredentials should be better enforced.
+
+ Adding authenticateSession() method that adds credentials to per-process credential storage
+ (for platforms that even have such). No Windows implementation, because writing another
+ loader for DRT is painful.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (authenticateSessionCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (-[SynchronousLoader dealloc]):
+ (-[SynchronousLoader connectionShouldUseCredentialStorage:]):
+ (-[SynchronousLoader connection:didReceiveAuthenticationChallenge:]):
+ (-[SynchronousLoader connection:didFailWithError:]):
+ (-[SynchronousLoader connectionDidFinishLoading:]):
+ (+[SynchronousLoader makeRequest:withUsername:password:]):
+ (LayoutTestController::authenticateSession):
+
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::authenticateSession):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::authenticateSession):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::authenticateSession):
+ Stub implementations.
+
+2010-04-21 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Make DRT compilable in Chromium tree
+ https://bugs.webkit.org/show_bug.cgi?id=37923
+
+ We need to use different GYPs in a case of WebKit-only checkout
+ and a case of whole Chromium checkout because the relative paths
+ from webkit/ to WebKit/chromium/features.gypi are different in
+ these cases and we can't use 'conditions' for 'includes' in GYPs.
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+
+2010-04-21 Jakub Wieczorek <jwieczorek@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ List item markers are not always updated after changes in the DOM.
+ https://bugs.webkit.org/show_bug.cgi?id=37060
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (markerTextForListItemCallback): A function that returns the marker text for a given list item.
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::markerTextForListItem): Implement it in the GTK port.
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::markerTextForListItem): Add a stub.
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::LayoutTestController):
+ (LayoutTestController::markerTextForListItem): Implement it in the Qt port.
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::markerTextForListItem): Add a stub.
+
+2010-04-21 Adam Roben <aroben@apple.com>
+
+ Exclude leaks in CGGradientCreateWithColorComponents from
+ run-webkit-tests leaks output
+
+ The leak in CG is covered by <rdar://problem/7888492>.
+
+ Fixes <http://webkit.org/b/37927>.
+
+ Reviewed by Eric Carlson.
+
+ * Scripts/old-run-webkit-tests:
+ (sub countAndPrintLeaks): Exclude leaks in
+ CGGradientCreateWithColorComponents on certain OSs.
+
+2010-04-21 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [DRT/Chromium] Import MockSpellCheck from Chromium
+ https://bugs.webkit.org/show_bug.cgi?id=37910
+
+ Import webkit/tools/test_shell/mock_spellcheck.{cc,h} rev.37241 of Chromium.
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+ Add ICU explicitly because WTFString.h includes ICU headers.
+ Add MockSpellCheck.{cpp,h}.
+ * DumpRenderTree/chromium/MockSpellCheck.cpp: Added.
+ * DumpRenderTree/chromium/MockSpellCheck.h: Added.
+ * DumpRenderTree/chromium/WebViewHost.cpp:
+ (WebViewHost::spellCheck):
+ * DumpRenderTree/chromium/WebViewHost.h:
+ * DumpRenderTree/chromium/config.h: Define JS_EXPORTDATA, which is used
+ by wtf/text/AtomicString.h included by wtf/text/WTFString.h.
+
+2010-04-21 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add webkit-patch pretty-diff
+ https://bugs.webkit.org/show_bug.cgi?id=37892
+
+ This is slightly lame because it asks you whether the diff is correct,
+ but it's a starting point.
+
+ * Scripts/webkitpy/tool/commands/__init__.py:
+ * Scripts/webkitpy/tool/commands/prettydiff.py: Added.
+ * Scripts/webkitpy/tool/main.py:
+
+2010-04-21 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ Remove mention of non-existant --no-build option
+ https://bugs.webkit.org/show_bug.cgi?id=37893
+
+ The option doesn't exist!
+
+ * Scripts/webkitpy/tool/commands/stepsequence.py:
+
+2010-04-21 Balazs Kelemen <kb@inf.u-szeged.hu>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Interrupting JavaScript is cumbersome when you use QtLaucher for testing or profiling.
+ https://bugs.webkit.org/show_bug.cgi?id=37198
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::toggleInterruptingJavaScriptEnabled):
+ (LauncherWindow::newWindow):
+ (LauncherWindow::cloneWindow):
+ (LauncherWindow::createChrome):
+ (main):
+ * QtLauncher/webpage.cpp:
+ (WebPage::WebPage):
+ (WebPage::shouldInterruptJavaScript):
+ * QtLauncher/webpage.h:
+ (WebPage::setInterruptingJavaScriptEnabled):
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, rolling out r57963.
+ http://trac.webkit.org/changeset/57963
+ https://bugs.webkit.org/show_bug.cgi?id=37759
+
+ Three tests started crashing on the Qt bot.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-21 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
+
+ Reviewed by Eric Seidel.
+
+ Make new-run-webkit-tests work for the Qt port
+ https://bugs.webkit.org/show_bug.cgi?id=37588
+
+ * Scripts/webkitpy/layout_tests/port/qt.py:
+
+2010-04-21 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests: try to detect alternate apache path
+ https://bugs.webkit.org/show_bug.cgi?id=37587
+
+ _check_port_build() also needs to return true in the
+ base implementation to not fail the check_build step.
+
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-21 Yi Shen <yi.4.shen@nokia.com>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Add LayoutTestController interface: computedStyleIncludingVisitedInfo
+ https://bugs.webkit.org/show_bug.cgi?id=37759
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::computedStyleIncludingVisitedInfo):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-21 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, test fix only.
+
+ new-run-webkit-tests: implement a --log trace message to be able to display detailed output of an individual test run
+ https://bugs.webkit.org/show_bug.cgi?id=37726
+
+ This change seems to have broken a test.
+ Attempting to handle the case where we don't have any
+ timing information. Dirk may have to correct this change.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests has much higher startup latency than run-webkit-tests
+ https://bugs.webkit.org/show_bug.cgi?id=37643
+
+ I got rid of the -expected.checksum reads during startup.
+ This makes startup noticably better on my laptop.
+
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ - Use image_hash() instead of .image_hash now that expected.checksum
+ file reads are done lazily.
+ * Scripts/webkitpy/layout_tests/port/http_server_base.py:
+ - Add debug logging for this sleep call.
+ In my testing I never saw this sleep() hit.
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ - Sleep a shorter interval to make websocket server
+ startup more responsive. On my machine startup was
+ taking around 1 second.
+ - Remove the unconditional .5s delay on startup.
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ - Make image_hash file reads done lazily in a new image_hash() function.
+ - Add a "Starting testing ..." meter update after DRT threads have
+ been started, but before we get updates from the first one.
+ - Rename variable "t" to a full english name to match WebKit style.
+
+2010-04-20 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Eric Seidel.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37748
+
+ Make Sheriffbot more inspirational.
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py:
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-20 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Import Chromium image_diff as ImageDiff
+ https://bugs.webkit.org/show_bug.cgi?id=37790
+
+ ImageDiff.cpp is based on tools/imagediff/image_diff.cc r41911 of Chromium.
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+ * DumpRenderTree/chromium/ImageDiff.cpp: Added.
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+
+2010-04-20 Dirk Pranke <dpranke@chromium.org>
+
+ This patch to new-run-webkit-tests adds a --log 'trace' option
+ that prints out detailed info about a given test as it executes
+ (where the baselines are, what the expectation is, what we got,
+ how long it took).
+
+ https://bugs.webkit.org/show_bug.cgi?id=37726
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ - use the newly exposed TestResult class and implement
+ --log trace
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_thread.py:
+ - rename TestStats to TestResult and make it more public, resulting
+ in cleaner code
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ - add expectation_to_string() as a separate callable function
+
+2010-04-20 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, rolling out r57907.
+ http://trac.webkit.org/changeset/57907
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ Appears to have broken MacEWS and possibly webkit-patch upload
+ for svn users. Needs further investigation.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ * Scripts/webkitpy/common/config/committers.py:
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+ * Scripts/webkitpy/common/net/statusserver.py:
+ * Scripts/webkitpy/common/prettypatch.py:
+ * Scripts/webkitpy/common/system/autoinstall.py:
+ * Scripts/webkitpy/common/system/deprecated_logging.py:
+ * Scripts/webkitpy/common/system/executive.py:
+ * Scripts/webkitpy/common/system/executive_unittest.py:
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ * Scripts/webkitpy/layout_tests/layout_package/metered_stream.py:
+ * Scripts/webkitpy/layout_tests/port/mac_unittest.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+ * Scripts/webkitpy/tool/steps/postdiffforcommit.py:
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+
+2010-04-20 Nate Chapin <japhet@chromium.org>
+
+ Unreviewed.
+
+ Update my irc handle in committers.py (natechapin -> japhet).
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-20 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ REGRESSION(57531): the commit-queue still hates Tor Arne Vestbø
+ https://bugs.webkit.org/show_bug.cgi?id=37765
+
+ I fixed the queue to not ignore Tor as a reviwer in r57531,
+ but instead it throws an exception every time his name is in a patch.
+
+ This fixes our Executive.run_command code to work around a Popen
+ bug http://bugs.python.org/issue5290 whereby python versions before 2.6
+ do not correctly handle unicode objects as input or output to
+ Popen.communicate.
+
+ Following the advice of:
+ http://farmdev.com/talks/unicode/
+ I'm attempting to take the python unicode plunge and use unicode()
+ objects as strings instead of str() objects everywhere in webkitpy.
+
+ We do not have to use u"" instead of "" because u"a" == "a" as expected
+ in Python. Python will generate a warning to the console in cases where
+ a unicode() == str() operation cannot be performed.
+
+ I also cleaned up the input handling in run_command a little by adding
+ a new _compute_input() method which can return early instead of having
+ such a long/cluttered if-block.
+
+ Executive.run* now correctly accept and return unicode() objects.
+ I attempted to fix all the places that we call .write() to make sure we
+ encode any unicode() objects into utf-8.
+
+ All places which use StringIO need to be sure to pass StringIO a
+ pre-encoded byte-array (str object) instead of unicode so that
+ clients which read from the StringIO don't have encoding exceptions.
+ To make this easier, I removed the patch_file_object support from
+ add_patch_to_bug, and changed the 4 places which previously used
+ StringIO to create a fake patch file.
+
+ I attempted to document any places where we are not correctly converting
+ to/from bytes (str() objects) to strings (unicode() objects).
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ - Read/write utf-8 files instead of ascii.
+ - Update the tests to use test for proper unicode() handling.
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+ - Use unicode() strings instead of str() byte arrays.
+ * Scripts/webkitpy/common/checkout/scm.py:
+ - Remove use of str().
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ - Read/write utf-8 files and use unicode() strings in testing.
+ * Scripts/webkitpy/common/config/committers.py:
+ - Use \u instead of \x to make slightly clearer what we're doing.
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ - Add a new _string_contents() method and explain why
+ we have to call unicode() on the result of soup.string
+ and why it's safe to do so w/o needing to pass a codec name.
+ - Remove the (unused) support for passing a file object to add_patch_to_bug().
+ * Scripts/webkitpy/common/net/buildbot.py:
+ - Use unicode() instead of str() when needing to coax a
+ NavigableString object into a unicode() object.
+ * Scripts/webkitpy/common/net/statusserver.py:
+ - Remove use of str()
+ * Scripts/webkitpy/common/prettypatch.py:
+ - Write out the patch file as utf-8.
+ * Scripts/webkitpy/common/system/autoinstall.py:
+ - Add a FIXME about encoding.
+ * Scripts/webkitpy/common/system/deprecated_logging.py:
+ - Document that tee() works on bytes, not strings.
+ * Scripts/webkitpy/common/system/executive.py:
+ - Make run* properly take and return unicode() objects.
+ * Scripts/webkitpy/common/system/executive_unittest.py:
+ - Added a unit test to make sure we don't break Tor again!
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ - Write out the test list as utf-8.
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ - Write out json files as utf-8.
+ * Scripts/webkitpy/layout_tests/layout_package/metered_stream.py:
+ - Add FIXME about encoding handling.
+ * Scripts/webkitpy/tool/commands/upload.py:
+ - Pass the diff directly to add_patch_to_bug instead of creating a StringIO file wrapper.
+ * Scripts/webkitpy/tool/mocktool.py:
+ - Rename add_patch_to_bug argument to match bugzilla.py
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+ - Pass the diff directly to add_patch_to_bug instead of creating a StringIO file wrapper.
+ * Scripts/webkitpy/tool/steps/postdiffforcommit.py: ditto.
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py: ditto.
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ - Fixed spurious logging seen when running test-webkitpy
+
+2010-04-20 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ For check-webkit-style, implemented __eq__() and __ne__() (the
+ built-in equality and inequality methods) for the
+ DefaultStyleErrorHandler class.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37850
+
+ This will facilitate unit-testing for a subsequent patch,
+ namely for https://bugs.webkit.org/show_bug.cgi?id=37850
+
+ * Scripts/webkitpy/style/error_handlers.py:
+ - Added __eq__() and __ne__() to the DefaultStyleErrorHandler
+ class.
+
+ * Scripts/webkitpy/style/error_handlers_unittest.py:
+ - Added unit tests for __eq__() and __ne__().
+ - Also included a minor clean-up refactoring of combining the
+ StyleErrorHandlerTestBase class (which has not needed to
+ be separate due to previous changes) into the
+ DefaultStyleErrorHandlerTest class.
+
+2010-04-20 Jakub Wieczorek <jwieczorek@webkit.org>
+
+ Unreviewed.
+
+ Add my IRC nick to the committers.py list.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-20 Kim Grönholm <kim.gronholm@nomovok.com>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Multitouch mocking in QtLauncher doesn't work with QGraphicsView
+ https://bugs.webkit.org/show_bug.cgi?id=37816
+
+ Fix multi-touch mocking in QtLauncher when using QGraphicsView.
+ Test: https://bug-32434-attachments.webkit.org/attachment.cgi?id=44955
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::eventFilter):
+ (LauncherWindow::initializeView):
+
+2010-04-20 MORITA Hajime <morrita@google.com>
+
+ Unreviewed, add myself to the committers list.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-20 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [DRT/Chromium] Fix some unexpected results of editing
+ https://bugs.webkit.org/show_bug.cgi?id=37843
+
+ This change fixes about 70 unexpected results.
+ The original test_webview_delegate.cc doesn't have this bug.
+ The bug was introduced when I ported it to WebKit tree.
+
+ * DumpRenderTree/chromium/WebViewHost.cpp:
+ (printRangeDescription): Replace the latter startContainer() with endContainer().
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed, build fix.
+
+ Turn off some unit tests for now - the new-run-webkit-tests dryrun
+ tests for chromium won't work if you don't have a chromium checkout.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37841
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed, build fix.
+
+ (Re-)add dryrun.py; this was renamed from passing.py in the previous
+ CL but apparently somehow didn't get checked in.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37841
+
+ * Scripts/webkitpy/layout_tests/port/dryrun.py: Added.
+
+2010-04-19 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by David Levin.
+
+ check-webkit-style: exits when encountering a deleted file
+ https://bugs.webkit.org/show_bug.cgi?id=37122
+
+ This reverts the quick fix done by r57119 and makes check_patch
+ not call check_file for deleted files.
+
+ Also this change fixes the behavior for "-", which should mean
+ stdin. Before this change, the style checker just ignored "-"
+ with a warning message.
+
+ * Scripts/webkitpy/style/checker.py:
+ * Scripts/webkitpy/style/checker_unittest.py:
+
+2010-04-19 Daniel Bates <dbates@rim.com>
+
+ No review, rolling out 57868.
+ http://trac.webkit.org/changeset/57868
+ https://bugs.webkit.org/show_bug.cgi?id=37748
+
+ Sheriffbot wasn't very inspirational after this patch.
+ Instead, he was silent when you said hi :-(. Rolling
+ out this patch so that I can debug/test this some more.
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py:
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-19 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Adam Barth.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37748
+
+ Make Sheriffbot more inspirational.
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py:
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-19 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix, add missing header.
+
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests - repurpose the "Passing" port as "Dryrun" port
+ that can be used to test platforms other than the one you are running
+ on. This can be useful for checking baselines and testing code
+ coverage.
+
+ Note that running the code on the "wrong" port requires each
+ port-specific implementation to actually not require any
+ platform-specific python code (e.g., the chromium-win port must
+ test for the existence of windows functions before calling them).
+
+ https://bugs.webkit.org/show_bug.cgi?id=37782
+
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/dryrun.py: Renamed from WebKitTools/Scripts/webkitpy/layout_tests/port/passing.py.
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests: add --build (default) and --no-build
+ options to make that step optional. This flag modifies what happens
+ in port.check_build().
+
+ https://bugs.webkit.org/show_bug.cgi?id=37786
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed, build fix.
+
+ new-run-webkit-tests - fix a typo in r57480 that caused us to stop
+ logging the actual list of unexpected results.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37831
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ From a patch by Tor Arne Vestbo <tor.arne.vestbo@nokia.com>
+
+ new-run-webkit-tests: make the retry step more explicit
+ https://bugs.webkit.org/show_bug.cgi?id=37606
+
+ It might be confusing to see the test and percent counters
+ reset without any notice of what's going on, so we make the
+ message that a retry-run is started explicit.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Sam Weinig <weinig@apple.com>
+
+ Reviewed by Anders Carlsson.
+
+ Add support for opening a new window (File->New Window) to
+ Windows MiniBrowser. Accelerator doesn't work.
+
+ * MiniBrowser/win/BrowserView.cpp:
+ (createNewPage): Use BrowserView::create.
+ * MiniBrowser/win/BrowserWindow.cpp:
+ (BrowserWindow::onCommand): Respond to ID_FILE_NEW_WINDOW
+ by creating a new window.
+ * MiniBrowser/win/BrowserWindow.h:
+ (BrowserWindow::create): Added. Don't allow creating
+ BrowserWindows on the stack by making constructor
+ private and exposing the create function.
+ * MiniBrowser/win/MiniBrowser.cpp:
+ (MiniBrowser::createNewWindow): Move new window creation
+ logic here.
+ * MiniBrowser/win/MiniBrowser.h:
+ * MiniBrowser/win/MiniBrowser.rc:
+ * MiniBrowser/win/main.cpp:
+ (_tWinMain): Use the new MiniBrowser::createNewWindow().
+
+2010-04-19 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Beth Dakin.
+
+ AX: aria-haspopup needs to be exposed
+ https://bugs.webkit.org/show_bug.cgi?id=37808
+
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+ (AccessibilityUIElement::hasPopup):
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Submit a better workaround for r57806 than the one in r57831 - log
+ an error and exit if you try to run new-run-webkit-tests with --use-drt
+ on Windows.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37822
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-04-19 Jesus Sanchez-Palencia <jesus@webkit.org>
+
+ Unreviewed.
+
+ Just adding myself as a committer.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Add slightly better logging to the websocket python wrapper script,
+ including a --verbose flag for debug output.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37233
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ * Scripts/new-run-webkit-websocketserver:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests: add a way (--print-unexpected-results) to
+ (re-)print out the tests that produced unexpected results in the
+ last run. Also add a way (--retry-unexpected-results) to
+ automatically re-test them.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37783
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Restructure the logging in new-run-webkit-tests so that many of log
+ messages that were logged to the MeteredStream also get logged in
+ --verbose mode.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37780
+
+ * Scripts/webkitpy/layout_tests/layout_package/metered_stream.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests: add 'summary' and 'unexpected-results' options
+ to the --log flag. Also add a 'progress' flag to enable the regular
+ progress bar (as opposed to 'detailed-progress', which enables the
+ dots). Also add a 'nothing' flag to allow you to be explicit that
+ you don't want any logging on the command line.
+
+ The default is
+ '--log detailed-progress,summary,unexpected,unexpected-results'
+
+ (The default logging is unchanged by this patch, this just makes things
+ properly configurable).
+
+ Note that 'nothing' doesn't work properly yet; I need a couple other
+ patches to land to avoid rewriting things several different ways.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37785
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-19 Kenneth Rohde Christiansen <kenneth@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ Make the URL change on committed load.
+
+ * QtLauncher/mainwindow.cpp:
+ (MainWindow::buildUI):
+ (MainWindow::setAddressUrl):
+ * QtLauncher/mainwindow.h:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix a typo in the rebaselining tool that causes us to use "debug"
+ instead of "Debug" in a directory path, which fails on platforms with
+ case-sensitive filesystems.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37819
+
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-04-19 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed, build fix.
+
+ Fix breakage of chromium-win canary bots caused by r57806. That patch
+ introduced the option of using Chrome's new port of DumpRenderTree,
+ but unfortunately that port relies on the webkit.py class
+ implementation which uses non-blocking I/O that isn't available on
+ Windows. This patch turns off that option and doesn't import the
+ class if we're running on Windows.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37817
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-04-19 James Robinson <jamesr@chromium.org>
+
+ Reviewed by abarth.
+
+ Fix a typo
+
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-04-19 Adam Roben <aroben@apple.com>
+
+ Fix run-webkit-tests when there are spaces in the path
+
+ Fixes <http://webkit.org/b/37809>
+
+ Reviewed by Adam Barth.
+
+ * Scripts/run-webkit-tests: Use an "indirect object" to specify the
+ path to the harness to exec(). According to perldoc, this usage will
+ prohibit perl from parsing the arguments to exec() via the shell,
+ which would incorrectly split paths with spaces in them, etc.
+
+2010-04-19 Dimitri Glazkov <dglazkov@chromium.org>
+
+ Reviewed by Adam Roben.
+
+ WinLauncher.h should use LF line-endings and use native line-endings style.
+ https://bugs.webkit.org/show_bug.cgi?id=37807
+
+ * WinLauncher/WinLauncher.h: Added property svn:eol-style, converted to LF line-endings.
+
+2010-04-19 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [DRT/Chromium] Fix a test initialization problem
+ https://bugs.webkit.org/show_bug.cgi?id=37791
+
+ * DumpRenderTree/chromium/DumpRenderTree.cpp:
+ (runTest): Call resetTestController() before runFileTest(). Some
+ controllers initialize their fields in reset() and not in their
+ constructors.
+
+2010-04-19 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ [Chromium] new-run-webkit-tests should use WebKitDriver for --use-drt
+ https://bugs.webkit.org/show_bug.cgi?id=37793
+
+ We need to use WebKitDriver instead of ChromiumDriver for Chromium
+ DRT because its interface is different from test_shell.
+
+ Chromium DRT has no UI. So we can't use it to show test results.
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-04-18 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Roben.
+
+ Make failure-reason more forgiving
+ https://bugs.webkit.org/show_bug.cgi?id=37525
+
+ Removed search_limit, which wasn't very useful anyway.
+ Added a log about the long load time loading from the builders.
+ Prompt the user for what revision to start walking from (makes it easy to restart upon failure).
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+
+2010-04-18 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Created a class for check-webkit-style that encapsulates iterating
+ over text files and reading them.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37754
+
+ This revision is an intermediate step towards separating our
+ style-checking code from the logic of iterating over files and
+ reading them.
+
+ * Scripts/webkitpy/common/system/logtesting.py:
+ - Added a logMessages() method to the LoggingTestCase class.
+ This method provides unit tests with access to the raw list
+ of log messages in case the tester needs to do something more
+ than simply assert the list of existing messages.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added a ProcessorBase class that processors of lists of lines
+ should eventually inherit from.
+ - Also added a FIXME to use the ProcessorBase class and the
+ TextFileReader class added below.
+
+ * Scripts/webkitpy/style/filereader.py: Added.
+ - Created a TextFileReader class that encapsulates reading
+ and iterating over text files.
+
+ * Scripts/webkitpy/style/filereader_unittest.py: Added.
+ - Added a TextFileReaderTest class to unit-test the
+ new TextFileReader class.
+
+2010-04-15 Tony Chang <tony@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ [chromium] new-run-webkit-tests should be able to use chromium DRT
+ https://bugs.webkit.org/show_bug.cgi?id=37645
+
+ Make sure that the lack of a chromium checkout doesn't cause the script to
+ fail.
+
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py: Fix up a few paths
+ to be relative to an upstream output dir.
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py: Add --use-drt flag.
+
+2010-04-18 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, fixing the Qt bot.
+
+ Add a layer of indirection when calling run-webkit-tests to allow testing new-run-webkit-tests on various platforms
+ https://bugs.webkit.org/show_bug.cgi?id=37632
+
+ * Scripts/run-webkit-tests:
+ - Exit non-zero of launching the harness fails.
+
+2010-04-18 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, fixing the Qt bot.
+
+ Add a layer of indirection when calling run-webkit-tests to allow testing new-run-webkit-tests on various platforms
+ https://bugs.webkit.org/show_bug.cgi?id=37632
+
+ * Scripts/run-webkit-tests:
+ - Fix the wrapper to work for users who do not
+ have WebKitTools/Scripts in their path.
+
+2010-04-14 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add a layer of indirection when calling run-webkit-tests to
+ allow testing new-run-webkit-tests on various platforms.
+ https://bugs.webkit.org/show_bug.cgi?id=37632
+
+ This will let us test and fix bugs in new-run-webkit-tests
+ without needing to restart the buildbot master between tests.
+
+ Currently this change leaves run-webkit-tests as-is, but once
+ its landed we will easily be able to turn on/off
+ new-run-webkit-tests for various ports/configurations.
+
+ I will send a note out to webkit-dev about how we will
+ be using this launcher script to test on the bots.
+
+ * Scripts/old-run-webkit-tests: Copied from WebKitTools/Scripts/run-webkit-tests.
+ * Scripts/run-webkit-tests:
+ - A new script which decides whether to run new- or old-
+ run-webkit-tests based on the platform.
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ - Add some dummy argument handling for arguments which
+ old-run-webkit-tests supports but new-run-webkit-tests
+ does not yet.
+
+2010-04-18 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add Gtk bots to the list of "core builders" (builders which stop the commit-queue when they turn red)
+ https://bugs.webkit.org/show_bug.cgi?id=33295
+
+ The Gtk builders have been green every time I've looked
+ at them in the last 5 days or so. It would appear webkit
+ is now keeping them green and we should update the core
+ builder list to match reality.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+
+2010-04-18 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Add support for LayoutTestController commands:
+ setSmartInsertDeleteEnabled
+ setSelectTrailingWhitespaceEnabled
+ execCommand
+ isCommandEnabled
+
+ https://bugs.webkit.org/show_bug.cgi?id=35844
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::resetSettings):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setSmartInsertDeleteEnabled):
+ (LayoutTestController::setSelectTrailingWhitespaceEnabled):
+ (LayoutTestController::execCommand):
+ (LayoutTestController::isCommandEnabled):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-17 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Remove steps_references and commands_references
+ https://bugs.webkit.org/show_bug.cgi?id=37758
+
+ We tried using the mumble_references convention to manage our
+ dependencies, but it doesn't seem to be providing much value for the
+ steps and commands module because these modules are small pieces of the
+ larger tool module. In this patch, I've removed the references file
+ for these modules.
+
+ I've left the style_references file for the style module because that
+ module seems better isolated from the rest of webkitpy and the
+ style_references file appears to be providing some value.
+
+ * Scripts/webkitpy/tool/commands/commandtest.py:
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py:
+ * Scripts/webkitpy/tool/commands/queries_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/commands_references.py: Removed.
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer_unittest.py:
+ * Scripts/webkitpy/tool/steps_references.py: Removed.
+
+2010-04-17 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ WebKit needs a Chromium Mac EWS Builder
+ https://bugs.webkit.org/show_bug.cgi?id=37742
+
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+
+2010-04-17 Adam Barth <abarth@webkit.org>
+
+ Fix expected results for unit test broken by
+ http://trac.webkit.org/changeset/57772
+
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+
+2010-04-17 Sam Weinig <weinig@apple.com>
+
+ Reviewed by Adam Roben.
+
+ Teach windows MiniBrowser how to work with window.open()
+ and targeted links.
+
+ * MiniBrowser/win/BrowserView.cpp:
+ (createNewPage): Create a new BrowserWindow and return its page.
+ (showPage): Show the page.
+ (closePage): Empty implementation.
+ (runJavaScriptAlert): Empty implementation.
+ (BrowserView::create): Register a UIClient.
+ * MiniBrowser/win/BrowserView.h:
+ (BrowserView::webView): Added.
+ Change create to take a BrowserWindow instead of an HWND.
+
+ * MiniBrowser/win/BrowserWindow.cpp:
+ (BrowserWindow::wndProc): Respond to WM_NCDESTROY.
+ (BrowserWindow::goToURL): Added. Forwards to BrowserView.
+ (BrowserWindow::onCreate): Don't always go to the default
+ url. Let the caller do this.
+ (BrowserWindow::onNCDestroy): Delete the window.
+ * MiniBrowser/win/BrowserWindow.h:
+ (BrowserWindow::view): Added.
+ (BrowserWindow::window): Added.
+
+ * MiniBrowser/win/main.cpp:
+ (_tWinMain):
+ Go to the default URL for the initial page. Allocate the initial
+ window on the heap for correctness.
+
+2010-04-16 Adam Roben <aroben@apple.com>
+
+ Add the Windows Debug (Test) builder to the list of core builders
+
+ It's been green for a few days now, and all the known Windows
+ flakiness is Release-only.
+
+ Rubber-stamped by Mark Rowe.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ (BuildBot.core_builder_name_regexps): Added a regular expression to
+ match the "Windows Debug (Test)" builder.
+
+2010-04-16 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Anders Carlsson.
+
+ Fix window.open() and targeted links.
+
+ * MiniBrowser/mac/BrowserWindowController.m:
+ (_createNewPage): Use the correct initializer to and load the window.
+
+2010-04-16 Adam Roben <aroben@apple.com>
+
+ Fix links to layout test results from build status pages
+
+ Reviewed by Mark Rowe.
+
+ * BuildSlaveSupport/build.webkit.org-config/master.cfg:
+ (ExtractTestResults.finished): Prepend "/" on the URL of the test
+ results page so that it is treated as an absolute URL.
+
+2010-04-16 Tony Chang <tony@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [chromium] build DRT on Linux
+ https://bugs.webkit.org/show_bug.cgi?id=37690
+
+ * Scripts/build-dumprendertree: Add support for win and linux
+
+2010-04-16 Sam Weinig <weinig@apple.com>
+
+ Reviewed by Adam Roben.
+
+ Use the threaded process model for MiniBrowser if holding down
+ the shift key on startup.
+
+ * MiniBrowser/win/BrowserView.cpp:
+ (BrowserView::create):
+
+2010-04-15 Tony Chang <tony@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [chromium] Add TestShellGtk.cpp so we can link on Linux
+ https://bugs.webkit.org/show_bug.cgi?id=37561
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp: Add new file and
+ add platform file exceptions.
+ * DumpRenderTree/chromium/TestShellGtk.cpp: Added.
+ (AlarmHandler):
+ (TestShell::waitTestFinished):
+
+2010-04-15 Tony Chang <tony@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ build DRT on chromium mac
+ https://bugs.webkit.org/show_bug.cgi?id=37639
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+ * Scripts/build-dumprendertree: enable build-dumprendertree --chromium
+
+2010-04-15 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add LayoutTestHelper for Mac
+ https://bugs.webkit.org/show_bug.cgi?id=37668
+
+ LayouTestHelper.mm is based on webkit/tools/test_shell/mac/layout_test_helper.mm
+ of Chromium.
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+ * DumpRenderTree/chromium/LayoutTestHelper.mm: Added.
+
+2010-04-15 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Geoff Garen.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37494
+ <rdar://problem/7857060> REGRESSION (r57340): fast/events/mouse-drag-from-frame-to-other-frame.html
+ fails on Windows
+
+ * DumpRenderTree/win/EventSender.cpp: (makeEventSender):
+ * DumpRenderTree/win/EventSender.h:
+ Tell EventSender if it's being created for a top frame.
+
+ * DumpRenderTree/win/FrameLoadDelegate.cpp:
+ (FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld): We only want to reset
+ EventSender machinery when a new test is loaded, not when an iframe (or just its global
+ object) is created.
+
+2010-04-15 Adam Roben <aroben@apple.com>
+
+ Fix Windows WebKit2 build.
+
+ * MiniBrowser/win/MiniBrowser.cpp:
+
+2010-04-15 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Include codereview issue number in patch description
+ https://bugs.webkit.org/show_bug.cgi?id=37677
+
+ This lets us know which rietveld issue this patch is tied to.
+
+ Also, make it so that --fancy-review overrides --no-review.
+
+ * Scripts/webkitpy/tool/steps/postcodereview.py:
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+
+2010-04-15 Adam Roben <aroben@apple.com>
+
+ Make --exit-after-n-failures work when all tests are timing out or crashing
+
+ Fixes <http://webkit.org/b/37679>.
+
+ Reviewed by Jon Honeycutt.
+
+ * Scripts/run-webkit-tests:
+ (top level): When a test crashes or times out, break out of the main loop if
+ stopRunningTestsEarlyIfNeeded returns true. Moved some code from the bottom of the main loop
+ from here...
+ (stopRunningTestsEarlyIfNeeded): ...to here.
+
+2010-04-15 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Anders Carlsson.
+
+ Add WebHistoryClient support.
+ https://bugs.webkit.org/show_bug.cgi?id=37671
+
+ Add HistoryClient logging.
+
+ * MiniBrowser/mac/BrowserWindowController.m:
+ (_didNavigateWithNavigationData):
+ (_didPerformClientRedirect):
+ (_didPerformServerRedirect):
+ (_didUpdateHistoryTitle):
+ (-[BrowserWindowController awakeFromNib]):
+
+2010-04-15 Anders Carlsson <andersca@apple.com>
+
+ Reviewed by Sam Weinig.
+
+ Start the mini browser in threaded mode if shift is pressed during startup.
+ https://bugs.webkit.org/show_bug.cgi?id=37670
+
+ * MiniBrowser/mac/AppDelegate.m:
+ (-[BrowserAppDelegate init]):
+
+2010-04-15 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Beth Dakin.
+
+ AXHelp is being appended from ancestors incorrectly
+ https://bugs.webkit.org/show_bug.cgi?id=37659
+
+ * DumpRenderTree/AccessibilityUIElement.cpp:
+ (getHelpTextCallback):
+ (AccessibilityUIElement::getJSClass):
+ * DumpRenderTree/AccessibilityUIElement.h:
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::helpText):
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+ (AccessibilityUIElement::helpText):
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::helpText):
+
+2010-04-15 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Re-format run-webkit-tests to fit in 80-columns for PEP-8 compliance.
+ (broken by r57463 and r57381, at least). I've also filed bug 37477
+ to fix check-webkit-style to catch these things.
+
+ https://bugs.webkit.org/show_bug.cgi?id=38586
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-15 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Add a way to override the user-visible name for the test binary since
+ some ports don't call it DumpRenderTree (e.g., Chromium Win uses
+ test_shell, Chromium Mac uses TestShell) by adding a driver_name()
+ method to the Port interface.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37631
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-14 Anders Carlsson <andersca@apple.com>
+
+ Reviewed by Adam Roben.
+
+ Add "Force Repaint" to debug menu.
+ https://bugs.webkit.org/show_bug.cgi?id=37627
+
+ * MiniBrowser/mac/BrowserWindowController.h:
+ * MiniBrowser/mac/BrowserWindowController.m:
+ (-[BrowserWindowController forceRepaint:]):
+ * MiniBrowser/mac/English.lproj/MainMenu.xib:
+
+2010-04-15 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Adam Roben.
+
+ Add debug-minibrowser script.
+
+ * Scripts/debug-minibrowser: Copied from Scripts/run-minibrowser.
+ * Scripts/webkitdirs.pm:
+
+2010-04-15 Roland Steiner <rolandsteiner@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Bug 37636 - [DRT/Chromium] Implement DRT/Chromium for Windows
+ https://bugs.webkit.org/show_bug.cgi?id=37636
+
+ Second patch: add Windows-specific implementation parts
+ of TestShell.
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+ * DumpRenderTree/chromium/TestShell.h:
+ (TestShell::finishedEvent):
+ * DumpRenderTree/chromium/TestShellWin.cpp:
+ (watchDogThread):
+ (TestShell::waitTestFinished):
+
+2010-04-15 Roland Steiner <rolandsteiner@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Bug 37636 - [DRT/Chromium] Implement DRT/Chromium for Windows
+ https://bugs.webkit.org/show_bug.cgi?id=37636
+
+ First patch: fix compiler errors.
+
+ * DumpRenderTree/chromium/EventSender.cpp:
+ (EventSender::reset):
+ (EventSender::dispatchMessage):
+ * DumpRenderTree/chromium/LayoutTestController.cpp:
+ (LayoutTestController::pathToLocalResource):
+ * DumpRenderTree/chromium/TestWebWorker.h:
+ * DumpRenderTree/chromium/TextInputController.cpp:
+ * DumpRenderTree/chromium/WebViewHost.h:
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+
+2010-04-14 Luiz Agostini <luiz.agostini@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Moving setViewMode from DumpRenderTreeSupportQt to qwebpage.cpp
+ https://bugs.webkit.org/show_bug.cgi?id=37622
+
+ Method qt_wrt_setViewMode was removed from qwebpage.cpp by mistake in r57433
+ (bug 35844). Moving it back.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setViewModeMediaFeature):
+
+2010-04-15 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Add some very minimal unit tests for new-run-webkit-tests. This should
+ be enough to catch egregious brokenness like syntax errors and import
+ declaration issues.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37432
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: Added.
+
+2010-04-14 Brian Weinstein <bweinstein@apple.com>
+
+ Reviewed by Adam Roben.
+
+ Add a way for the buildbot to kill any old processes that are running. This
+ is useful because the Windows bots can get in states where a process remains
+ running (httpd.exe, DumpRenderTree.exe), which causes the bots to get in a red
+ state, and the processes must be killed manually.
+
+ * BuildSlaveSupport/build.webkit.org-config/master.cfg: If we are on Windows, kill
+ the old processes that might be running.
+ * BuildSlaveSupport/win/kill-old-processes: Added.
+
+2010-04-14 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Anders Carlsson.
+
+ Tear down WebKit more completely on window closing and application
+ termination. We still don't block application termination for pending
+ close, but this is a step in the right direction.
+
+ * MiniBrowser/mac/AppDelegate.m:
+ (-[BrowserAppDelegate applicationWillTerminate:]):
+ * MiniBrowser/mac/BrowserWindowController.h:
+ * MiniBrowser/mac/BrowserWindowController.m:
+ (-[BrowserWindowController dealloc]):
+ (-[BrowserWindowController windowWillClose:]):
+ (-[BrowserWindowController applicationTerminating]):
+
+2010-04-14 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Use pretty patch for confirming webkit-patch diffs
+ https://bugs.webkit.org/show_bug.cgi?id=37489
+
+ * Scripts/webkitpy/common/prettypatch.py: Added.
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/confirmdiff.py:
+
+2010-04-14 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Teach webkit-patch how to handle revisions missing ChangeLogs
+ https://bugs.webkit.org/show_bug.cgi?id=37519
+
+ Make commit_info_for_revision return None when revision
+ is missing a ChangeLog. Previously we would throw an array index
+ exception.
+ Teach callers how to handle None.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-04-13 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Roben.
+
+ Add Qt Bot to the list of "core builders" (builders which block the commit-queue when red)
+ https://bugs.webkit.org/show_bug.cgi?id=33297
+
+ This is an experiment. The bots have been green for
+ a while. We'll see if adding them under sheriff-bot protection
+ will keep them green.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+
+2010-04-13 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, just fixing a constant in the Rietveld unit test.
+
+ * Scripts/webkitpy/common/net/rietveld_unittest.py
+
+2010-04-13 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, just adding missing Mock to fix python tests.
+
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-13 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by David Levin.
+
+ Add experimental prototype Rietveld integration to webkit-patch upload
+ https://bugs.webkit.org/show_bug.cgi?id=37418
+
+ This patch adds bare-bones integration with Rietveld for code reviews.
+ The behavior is hidden behind the --fancy-review command line flag.
+ Currently, there's no support for uploading more than one patch per
+ issue (which is a nice feature of Rietveld). The plan is to play with
+ this for a bit and see if it's useful.
+
+ Modified from Adam's original patch to autoinstall the rietveld upload script.
+
+ * Scripts/webkitpy/common/config/__init__.py:
+ * Scripts/webkitpy/common/net/rietveld.py: Added.
+ * Scripts/webkitpy/common/net/rietveld_unitttest.py: Added.
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/__init__.py:
+ * Scripts/webkitpy/tool/steps/options.py:
+ * Scripts/webkitpy/tool/steps/postcodereview.py: Added.
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+
+2010-04-13 Sam Weinig <sam@webkit.org>
+
+ Rubber-stamped by Mark Rowe.
+
+ Add Makefile to MiniBrowser.
+
+ * MiniBrowser/Makefile: Added.
+
+2010-04-13 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, just adding a sanity check.
+
+ Add check to make sure commit-queue can never commit too short a message
+ https://bugs.webkit.org/show_bug.cgi?id=37528
+
+ The commit-queue made bogus messages here:
+ http://trac.webkit.org/changeset/57532
+ http://trac.webkit.org/changeset/57534
+
+ This was a regression caused by adding unicode parsing for
+ our ChangeLog files. Popen does not seem to play nice with
+ unicode strings.
+
+ I'm also adding an "assert" to make sure short ChangeLogs never happen again.
+
+ * Scripts/webkitpy/common/system/executive.py:
+ - Cast input to strings before passing to POpen
+ * Scripts/webkitpy/tool/steps/commit.py:
+ - Validate that commit messages are not to short.
+
+2010-04-13 Adam Roben <aroben@apple.com>
+
+ Robustify new-run-webkit-tests against paths with spaces in them
+
+ Reviewed by Eric Seidel.
+
+ * Scripts/webkitpy/layout_tests/port/apache_http_server.py:
+ (LayoutTestApacheHttpd.__init__): Quote all paths that we pass to
+ Apache to ensure that paths with spaces in them are interpreted
+ correctly.
+
+2010-04-13 Csaba Osztrogonác <ossy@webkit.org>
+
+ Unreviewed buildfix after r57537.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.h: Declaration of removeOriginAccessWhitelistEntry() added.
+
+2010-04-13 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Maciej Stachowiak.
+
+ Make building new webkit API and MiniBrowser a little easier.
+
+ * Scripts/build-webkit: Make building with --webkit2 build the
+ MiniBrowser as well and tell you how to use it.
+ * Scripts/run-minibrowser: Copied from Scripts/run-safari.
+ * Scripts/webkitdirs.pm: Add runMiniBrowser function.
+
+2010-04-12 Timothy Hatcher <timothy@apple.com>
+
+ SecurityOrigin needs a way to remove individual OriginAccessEntries
+ https://bugs.webkit.org/show_bug.cgi?id=37449
+
+ Reviewed by Dave Hyatt.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (removeOriginAccessWhitelistEntryCallback): Added. Call LayoutTestController::removeOriginAccessWhitelistEntry.
+ (LayoutTestController::staticFunctions): Added removeOriginAccessWhitelistEntry.
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::removeOriginAccessWhitelistEntry): Added. FIXME to implement.
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::removeOriginAccessWhitelistEntry): Added.
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::removeOriginAccessWhitelistEntry): Added. FIXME to implement.
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::removeOriginAccessWhitelistEntry): Added.
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::whiteListAccessFromOrigin): FIXME to implement.
+ (LayoutTestController::removeOriginAccessWhitelistEntry): Added. FIXME to implement.
+
+2010-04-13 Timothy Hatcher <timothy@apple.com>
+
+ Rename SecurityOrigin::whiteListAccessFromOrigin to addOriginAccessWhitelistEntry.
+ And LayoutTestController.whiteListAccessFromOrigin to addOriginAccessWhitelistEntry.
+ And SecurityOrigin::resetOriginAccessWhiteLists to resetOriginAccessWhitelists.
+
+ SecurityOrigin needs a way to remove individual OriginAccessEntries
+ https://bugs.webkit.org/show_bug.cgi?id=37449
+
+ Reviewed by Dave Hyatt.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (addOriginAccessWhitelistEntryCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/chromium/LayoutTestController.cpp:
+ (LayoutTestController::LayoutTestController):
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+ * DumpRenderTree/chromium/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetWebViewToConsistentStateBeforeTesting):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ * DumpRenderTree/win/DumpRenderTree.cpp:
+ (resetWebViewToConsistentStateBeforeTesting):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::addOriginAccessWhitelistEntry):
+
+2010-04-13 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
+
+ Reviewed by Eric Seidel.
+
+ Remove duplicate function for new-run-webkit-tests
+ https://bugs.webkit.org/show_bug.cgi?id=37517
+
+ The version() function was already implemented.
+
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-13 Eric Seidel <eric@webkit.org>
+
+ Reviewed by David Levin.
+
+ the commit-queue hates Tor Arne Vestbø
+ https://bugs.webkit.org/show_bug.cgi?id=37511
+
+ We were failing to read reviewers out of ChangeLogs
+ when the reviewer has unicode characters in his/her name.
+ I fixed this by explicitly decoding from utf8 every time we
+ read in a ChangeLog file (they are always UTF8).
+
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+
+2010-04-13 Adam Roben <aroben@apple.com>
+
+ Fix run-webkit-tests on Windows with spaces in the path
+
+ Fixes <http://webkit.org/b/37509>.
+
+ Reviewed by Steve Falkenburg.
+
+ * Scripts/run-webkit-tests:
+ (convertPathUsingCygpath): Remove spaces from the path before passing
+ them to cygpath, then add them back in after conversion, as some
+ versions of cygpath seem to convert spaces into newlines.
+
+2010-04-13 Adam Barth <abarth@webkit.org>
+
+ Unreviewed, but approved by Dumitru Daniliuc. (This patch is intended
+ to fix the downstream Chromium build bots. Hopefully it will work!)
+
+ Add a driver script for the new websocket server
+ https://bugs.webkit.org/show_bug.cgi?id=37495
+
+ websocket_server.py can't be run directly because its a module and not
+ a standalone script. This used to work by accident because it didn't
+ depend on any other modules.
+
+ * Scripts/new-run-webkit-websocketserver: Added.
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-04-12 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Make new-run-webkit-test PrettyPatch failure reporting more awesome
+ https://bugs.webkit.org/show_bug.cgi?id=37487
+
+ I also fixed an Executive/executive typo.
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+
+2010-04-12 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests should only build java support files on Mac
+ https://bugs.webkit.org/show_bug.cgi?id=37482
+
+ Only the mac needs java support files, so I pushed _build_java
+ down into the Mac port using a new hook "_check_port_build".
+ In the process I noticed a bunch of code which could be shared
+ between all ports and thus got rid of _tests_for_disabled_features
+ and version() copy/paste between all webkit ports.
+ I also made check_build only bother to check for ImageDiff if we're
+ using pixel tests.
+
+ * Scripts/webkitpy/layout_tests/port/gtk.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/qt.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ * Scripts/webkitpy/layout_tests/port/win.py:
+
+2010-04-12 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, trying to make scripts work on machines without
+ Ruby...
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+
+2010-04-12 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
+
+ Reviewed by Eric Seidel.
+
+ Add stub files for running new-run-webkit-tests for the Qt port
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/qt.py: Added.
+
+2010-04-12 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Modify run_webkit_tests.py to not call sys.exit() at the end of test
+ run; doing so makes it more difficult to embed the routine for,
+ among other things, unit tests. We push the exit calling up into
+ new-run-webkit-tests.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37464
+
+ * Scripts/new-run-webkit-tests:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-12 Eric Seidel <eric@webkit.org>
+
+ Unreviewed.
+
+ Add stub Gtk implementation for new-run-webkit-tests.
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/gtk.py: Added.
+
+2010-04-12 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests fails with exception on systems missing ruby
+ https://bugs.webkit.org/show_bug.cgi?id=37441
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ - Catch failures similar to how wdiff code path does.
+ - After one failure, stop trying.
+
+2010-04-12 Tony Chang <tony@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix how we import simplejson based on how it's used in this file.
+ This fixes exceptions raised when trying to write the simplejson output.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-11 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Refactored check-webkit-style so that the StyleChecker class
+ has no dependencies on patch-related concepts.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37065
+
+ This patch is an intermediate step towards making the StyleChecker
+ class a generalized file processor that can do arbitary operations
+ on the files corresponding to a list of paths. This patch
+ also simplifies the unit-testing of patch-checking code.
+
+ * Scripts/check-webkit-style:
+ - Updated to use the new PatchChecker class.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Refactored the StyleChecker.check_patch() method into the
+ check() method of a new PatchChecker class.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Refactored the unit tests as necessary, changing the
+ StyleCheckerCheckPatchTest class to a PatchCheckerTest class.
+
+2010-04-11 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Fix new-run-webkit-tests regressions cased by Eric's option parsing patch
+ https://bugs.webkit.org/show_bug.cgi?id=37430
+
+ We need some basic unit testing of this script, or we're going to keep
+ breaking it like this. Added missing namespace qualifiers and
+ propagated renaming of an option.
+
+ * Scripts/webkitpy/common/config/ports.py:
+ * Scripts/webkitpy/layout_tests/driver_test.py:
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-11 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Break new-run-webkit-tests options into groups for easier re-use and possible relocation
+ https://bugs.webkit.org/show_bug.cgi?id=37408
+
+ new-run-webkit-tests currently has one huge function for
+ dealing with all options-parsing.
+ This patch is a first attempt at trying to split that large
+ function down into smaller (hopefully more readable?) chunks
+ dealing with the different areas of options.
+ For example, it would make sense to move configuration
+ options off into some module which deals with the vagries of
+ WebKit's configuration system. It would also make sense to move
+ Chromium options off onto the Chromium port object (where they are used).
+ It may make sense to move results.json options over to the results.json code.
+ This change is a first iteration, and we will certainly need more
+ refinement to this code over time. Hopefully I didn't make things
+ harder to read here.
+
+ * Scripts/webkitpy/layout_tests/driver_test.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-11 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ create-rollout copy needs to be updated to reflect removal of --no-build
+ https://bugs.webkit.org/show_bug.cgi?id=37425
+
+ Removed --no-build and --no-test from the instructions because these
+ don't exist anymore.
+
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ - Updated the expected results to reflect the new copy.
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+
+2010-04-11 Sheriff Bot <webkit.review.bot@gmail.com>
+
+ Unreviewed, rolling out r57460.
+ http://trac.webkit.org/changeset/57460
+ https://bugs.webkit.org/show_bug.cgi?id=37424
+
+ broke chromium builders (Requested by tony^work on #webkit).
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp:
+
+2010-04-11 Tony Chang <tony@chromium.org>
+
+ Reviewed by Darin Fisher.
+
+ [chromium] update chromium DEPS for upstream compile
+ https://bugs.webkit.org/show_bug.cgi?id=36578
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp: Small fix to avoid a circular dependency between
+ WebKit.gyp and webkit.gyp.
+
+2010-04-11 Joseph Pecoraro <joepeck@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ svn-apply errors out when removing directories in git
+ https://bugs.webkit.org/show_bug.cgi?id=34871
+
+ * Scripts/svn-apply:
+ (isDirectoryEmptyForRemoval): early break if the directory doesn't exist
+ (scmRemove): have git ignore unmatched files
+
+2010-04-11 Daniel Bates <dbates@rim.com>
+
+ No review, rolling out 57440.
+ http://trac.webkit.org/changeset/57440
+ https://bugs.webkit.org/show_bug.cgi?id=27204
+
+ Did not handle Git patches that included both file and property
+ changes to the same file. Rolling this change out while I look
+ into this.
+
+ * Scripts/VCSUtils.pm:
+ * Scripts/svn-apply:
+ * Scripts/svn-unapply:
+ * Scripts/webkitperl/VCSUtils_unittest/appendSVNExecutableBitChangeToPatch.pl: Removed.
+ * Scripts/webkitperl/VCSUtils_unittest/parseGitFileMode.pl: Removed.
+ * Scripts/webkitperl/VCSUtils_unittest/parseStartOfPatchOrPropertyChangeAndEndOfPropertyChange.pl: Removed.
+
+2010-04-11 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix typo in log path for AbstractQueue
+ https://bugs.webkit.org/show_bug.cgi?id=37414
+
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+
+2010-04-11 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Add missing import statement.
+
+ * Scripts/webkitpy/common/system/executive.py:
+
+2010-04-11 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Add setWillSendRequestReturnsNull and setWillSendRequestClearHeader
+
+ https://bugs.webkit.org/show_bug.cgi?id=37410
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::reset):
+ (LayoutTestController::setWillSendRequestReturnsNull):
+ (LayoutTestController::setWillSendRequestClearHeader):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-11 Csaba Osztrogonác <ossy@webkit.org>
+
+ [Qt] Unreviewed buildfix for --debug build after r57433.
+
+ Refactor Qt DRT support in QtWebKit
+ https://bugs.webkit.org/show_bug.cgi?id=35844
+
+ * QtLauncher/main.cpp: qt_drt_garbageCollector_collect(); renamed to DumpRenderTreeSupportQt::garbageCollectorCollect();
+ (launcherMain):
+
+2010-04-11 Joseph Pecoraro <joepeck@webkit.org>
+
+ Reviewed by Mark Rowe.
+
+ Make commit-log-editor Rubber-stamp aware. And other minor cleanups.
+ https://bugs.webkit.org/show_bug.cgi?id=37407
+
+ * Scripts/commit-log-editor:
+
+2010-04-11 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add PrettyPatch links to new-run-webkit-tests output
+ https://bugs.webkit.org/show_bug.cgi?id=37406
+
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ - We're leaking a file handle here, add a FIXME.
+ * Scripts/webkitpy/layout_tests/layout_package/test_failures.py:
+ - Add pretty diff links.
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ - Add support for generating pretty diffs using PrettyPatch.
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ - We're leaking another file handle here, another FIXME.
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+ - Update write_output_files signature.
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ - Remove unused arguments from write_output_files.
+ - Add support for dumping pretty diffs to write_output_files.
+ - Fix a bunch of file descriptor leaks in this file.
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py:
+ - Update write_output_files signature.
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ kill_process is copy/pasted in five places
+ https://bugs.webkit.org/show_bug.cgi?id=37405
+
+ We shouldn't replicate the kill_process logic in every port. Instead,
+ we should move the process interaction to Executive.
+
+ Dirk mentioned that he wanted this abstraction to make it easier to
+ mock things out for testing. It turns out this function is only used
+ in one place where it can't be used as a mock point for testing because
+ the corresponding create process actually creates a real process. In
+ the long term, we should indirect both these calls through a non-static
+ Executive as a mock point. However, we should wait on that until we
+ actually want to write the test.
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ * Scripts/webkitpy/layout_tests/port/win.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests shouldn't alter its path to import packages
+ https://bugs.webkit.org/show_bug.cgi?id=37404
+
+ * Scripts/new-run-webkit-tests:
+ * Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py:
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py:
+ * Scripts/webkitpy/thirdparty/simplejson/decoder.py:
+
+2010-04-10 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests should store results to a directory under the build tree
+ https://bugs.webkit.org/show_bug.cgi?id=37380
+
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-10 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Eric Seidel.
+
+ https://bugs.webkit.org/show_bug.cgi?id=27204
+
+ Implement support for changing the executable bit of a file.
+ The executable bit is among the most changed file properties.
+ Future support can include other property changes.
+
+ Currently, if a patch changes the executable bit of a file
+ it is not respected by svn-apply or svn-unapply. Since the
+ commit-queue bot uses these tools as part of its workflow,
+ such patches cannot be committed by it. That is, such patches
+ need to be committed by hand. Instead, we should add support
+ for the executable bit so that such patches can be committed
+ by the commit-queue bot.
+
+ * Scripts/VCSUtils.pm: Also change reference to Apple Computer, Inc.
+ in copyright to Apple, Inc.
+ * Scripts/svn-apply:
+ * Scripts/svn-unapply:
+ * Scripts/webkitperl/VCSUtils_unittest/appendSVNExecutableBitChangeToPatch.pl: Added.
+ * Scripts/webkitperl/VCSUtils_unittest/parseGitFileMode.pl: Added.
+ * Scripts/webkitperl/VCSUtils_unittest/parseStartOfPatchOrPropertyChangeAndEndOfPropertyChange.pl: Added.
+
+2010-04-10 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ WinEWS bot fails to svn update because scm.clean_working_directory leaves files around
+ https://bugs.webkit.org/show_bug.cgi?id=37401
+
+ The Git-based bots don't have this trouble because
+ Git.clean_working_directory fully removes files that were
+ marked as "add". SVN.clean_working_directory previously just
+ called "svn revert" which would leave added files in the
+ working directory untracked. This patch makes
+ SVN.clean_working_directory function more like
+ Git.clean_working_directory by removing added files after revert.
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+ - Add SCM.absolute_path for easy conversion between
+ repository-relative paths and absolute paths.
+ - Add SCM.add and SCM.added_files
+ - Make SVN.clean_working_directory remove any added_files after svn revert.
+ - The new unit tests found a bug in Git.status_command, change to use git diff --name-status instead.
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ - Add tests for added code.
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests can't find ImageDiff on Windows
+ https://bugs.webkit.org/show_bug.cgi?id=37403
+
+ It turns out the build directory on Windows is structured differently
+ than it is on other platforms. Instead of assuming the normal
+ structure, we should just ask perl to figure it out for us.
+
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Rubber-stamped by Eric Seidel.
+
+ Change "Gathering files" status line to "Collecting tests". Gathering
+ the files sounds silly to me.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix bugs to make new-run-webkit-tests almost run on windows
+ https://bugs.webkit.org/show_bug.cgi?id=37400
+
+ Fix some minor bugs that prevent new-run-webkit-tests from being run on
+ Windows. I still haven't run it to completion, but I'm getting
+ further.
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+
+2010-04-10 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Refactor Qt DRT support in QtWebKit
+
+ Update Qt DRT to use new DumpRenderTreeSupportQt static class.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35844
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::DumpRenderTree):
+ (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting):
+ (WebCore::DumpRenderTree::dumpFramesAsText):
+ * DumpRenderTree/qt/DumpRenderTreeQt.h:
+ * DumpRenderTree/qt/GCControllerQt.cpp:
+ (GCController::collect):
+ (GCController::collectOnAlternateThread):
+ (GCController::getJSObjectCount):
+ * DumpRenderTree/qt/GCControllerQt.h:
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::counterValueForElementById):
+ (LayoutTestController::setViewModeMediaFeature):
+ (LayoutTestController::setMediaType):
+ (LayoutTestController::closeWebInspector):
+ (LayoutTestController::showWebInspector):
+ (LayoutTestController::evaluateInWebInspector):
+ (LayoutTestController::setFrameFlatteningEnabled):
+ (LayoutTestController::setJavaScriptProfilingEnabled):
+ (LayoutTestController::setTimelineProfilingEnabled):
+ (LayoutTestController::pauseAnimationAtTimeOnElementWithId):
+ (LayoutTestController::pauseTransitionAtTimeOnElementWithId):
+ (LayoutTestController::sampleSVGAnimationForElementAtTime):
+ (LayoutTestController::numberOfActiveAnimations):
+ (LayoutTestController::whiteListAccessFromOrigin):
+ (LayoutTestController::setCaretBrowsingEnabled):
+ (LayoutTestController::setDomainRelaxationForbiddenForURLScheme):
+ (LayoutTestController::workerThreadCount):
+ (LayoutTestController::pageNumberForElementById):
+ (LayoutTestController::numberOfPages):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move global queue log to the logs directory so it survives git clean -f
+ https://bugs.webkit.org/show_bug.cgi?id=37395
+
+ * Scripts/webkitpy/tool/commands/queues.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ SheriffBot should spam when it encounters errors
+ https://bugs.webkit.org/show_bug.cgi?id=37329
+
+ We need to always update the status server so we don't get stuck in a
+ spam loop. I tried writing a test for this change, but it kind of
+ got out of control. We need a better way to do failure injection.
+
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Unreviewed attempt to fix the Chromium Mac canary.
+
+ * Scripts/webkitpy/common/config/ports.py:
+ * Scripts/webkitpy/common/config/ports_unittest.py:
+ * Scripts/webkitpy/common/system/executive.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Add the Apache bits to win.py for new-run-webkit-tests
+ https://bugs.webkit.org/show_bug.cgi?id=37397
+
+ I still have run this yet, but I looked around to figure out what the
+ various paths appear to be. I'll figure out a way to remove the
+ copy/paste code in a future patch.
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ * Scripts/webkitpy/layout_tests/port/win.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Sketch out the win port for new-run-webkit-tests
+ https://bugs.webkit.org/show_bug.cgi?id=37393
+
+ I haven't tried running this yet, but we've got to start somewhere.
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py:
+ * Scripts/webkitpy/layout_tests/port/win.py: Added.
+
+2010-04-10 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ webkit-patch land should not build and test by default
+ https://bugs.webkit.org/show_bug.cgi?id=33631
+
+ Reverse the sense of --no-build and --no-test to be --build and --test.
+ Also, decoupled the build and test options so you can test without
+ building.
+
+ (Patch manngled by Adam Barth. All bugs are his fault.)
+
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/steps/options.py:
+ * Scripts/webkitpy/tool/steps/runtests.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Factor WebKitPort out of MacPort to allow for WinPort
+ https://bugs.webkit.org/show_bug.cgi?id=37388
+
+ The split is a bit of a guess. We might have to adjust things once we
+ actually have a second port to work with.
+
+ * Scripts/webkitpy/layout_tests/port/apache_http_server.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/webkit.py: Added.
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-04-10 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ run_webkit_tests.py shouldn't have platform-specific logic
+ https://bugs.webkit.org/show_bug.cgi?id=37387
+
+ Dirk Pranke pointed out that my last patch was wrong because I
+ introduced platform-specific logic into run_webkit_tests.py, limiting
+ the parallelism in Chromium to work around a bug in the main Mac port.
+
+ * Scripts/webkitpy/common/system/executive.py:
+ - Fix a typo pointed out by Chris Jerdonek.
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-10 Robert Hogan <robert@webkit.org>
+
+ Unreviewed fix to regressions in r57416.
+
+ [Qt] Fix regressions in http/tests/navigation from r57416
+
+ Reset willSendRequestReturnsNullOnRedirect after each test to
+ prevent it leaking to subsequent tests.
+
+ Error pointed out by Jakub Wieczorek.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37237
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::reset):
+
+2010-04-11 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Update layoutTestController.DumpResourceLoadCallbacks to match other ports.
+
+ Add support for layoutTestController.setWillSendRequestReturnsNullOnRedirect to Qt DRT.
+ Prevent dumping resource load callbacks once layout test has dumped.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37237
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::dump):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setWillSendRequestReturnsNullOnRedirect):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-10 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Implement Desktop Notifications API for QtWebKit
+ https://bugs.webkit.org/show_bug.cgi?id=35503
+
+ DRT stubs for notification. Enables to run and pass
+ 3 (currently disabled) tests.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::LayoutTestController):
+ (LayoutTestController::grantDesktopNotificationPermission):
+ (LayoutTestController::checkDesktopNotificationPermission):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-04-09 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, another change to executive.py to make it run with
+ python 2.4.
+
+ * Scripts/webkitpy/common/system/executive.py:
+
+2010-04-09 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, attempting to make executive.py run with python 2.4
+ (which is still used on Chromium's Windows canary bot).
+
+ * Scripts/webkitpy/common/system/executive.py:
+
+2010-04-09 Kevin Watters <kevinwatters@gmail.com>
+
+ Reviewed by Eric Seidel.
+
+ [wx] Basic implementation of SVG support for wx port.
+
+ * wx/build/settings.py:
+
+2010-04-09 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Don't reinvent Executive.cpu_count for every port
+ https://bugs.webkit.org/show_bug.cgi?id=37377
+
+ mac.py and chromium_mac.py had some copy/paste code. This code doesn't
+ actually have anything to do with WebKit ports. It's really just
+ something in the multiprocessing package. The lame bit is that package
+ isn't available in older versions of Python, so we need to implement a
+ fallback. However, we already have the fallback in common. We don't
+ need to reinvent it specificly for layout_tests.
+
+ * Scripts/webkitpy/common/system/executive.py:
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/test.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-09 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests --release fails to build release DRT when global configuration is Debug
+ https://bugs.webkit.org/show_bug.cgi?id=37376
+
+ We need to explicitly pass the --release flag. I bet there are more
+ instances of this bug.
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-04-09 Tony Chang <tony@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ [chromium] DRT compile fix on win/linux
+ https://bugs.webkit.org/show_bug.cgi?id=37314
+
+ Looks like this was missed when upstreaming.
+
+ * DumpRenderTree/chromium/EventSender.cpp:
+ (applyKeyModifier):
+
+2010-04-09 Adam Barth <abarth@webkit.org>
+
+ Unreviewed, but approved by Dirk Pranke.
+
+ rename test_expectations_test.py to test_expectations_unittest.py so it actually gets run
+ https://bugs.webkit.org/show_bug.cgi?id=37372
+
+ We need to end unit tests with _unittest.py for them to be autodetected
+ by the test harness. +6 tests.
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py: Renamed from WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_test.py.
+
+2010-04-09 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests should talk about DumpRenderTree not test_shell
+ https://bugs.webkit.org/show_bug.cgi?id=37371
+
+ test_shell is some strange Chromium thing.
+ DumpRenderTree (tm) is the real deal.
+
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py: Added.
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_failures.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py: Removed.
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/server_process.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-09 Zoltan Horvath <zoltan@webkit.org>
+
+ Reviewed by Alexey Proskuryakov.
+
+ Fix 2 issues (what were introduced in r56524) in svn-create-patch's generateDiff()
+ https://bugs.webkit.org/show_bug.cgi?id=32582
+
+ Add missing return variable. Initialize $patch variable and remove unnecessary condition.
+
+ * Scripts/svn-create-patch:
+
+2010-04-09 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix after addition of LayoutTestController method.
+
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::computedStyleIncludingVisitedInfo):
+
+2010-04-09 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Anders Carlsson.
+
+ Fix for https://bugs.webkit.org/show_bug.cgi?id=37368
+ Add MiniBrowser.
+
+ * MiniBrowser: Added.
+ * MiniBrowser/MiniBrowser.vcproj: Added.
+ * MiniBrowser/MiniBrowser.xcodeproj: Added.
+ * MiniBrowser/MiniBrowser.xcodeproj/project.pbxproj: Added.
+ * MiniBrowser/mac: Added.
+ * MiniBrowser/mac/AppDelegate.h: Added.
+ * MiniBrowser/mac/AppDelegate.m: Added.
+ * MiniBrowser/mac/BrowserWindowController.h: Added.
+ * MiniBrowser/mac/BrowserWindowController.m: Added.
+ * MiniBrowser/mac/English.lproj: Added.
+ * MiniBrowser/mac/English.lproj/BrowserWindow.xib: Added.
+ * MiniBrowser/mac/English.lproj/InfoPlist.strings: Added.
+ * MiniBrowser/mac/English.lproj/MainMenu.xib: Added.
+ * MiniBrowser/mac/MiniBrowser-Info.plist: Added.
+ * MiniBrowser/mac/MiniBrowser_Prefix.pch: Added.
+ * MiniBrowser/mac/main.m: Added.
+ * MiniBrowser/win: Added.
+ * MiniBrowser/win/BrowserView.cpp: Added.
+ * MiniBrowser/win/BrowserView.h: Added.
+ * MiniBrowser/win/BrowserWindow.cpp: Added.
+ * MiniBrowser/win/BrowserWindow.h: Added.
+ * MiniBrowser/win/MiniBrowser.cpp: Added.
+ * MiniBrowser/win/MiniBrowser.h: Added.
+ * MiniBrowser/win/MiniBrowser.rc: Added.
+ * MiniBrowser/win/Resources: Added.
+ * MiniBrowser/win/main.cpp: Added.
+ * MiniBrowser/win/resource.h: Added.
+ * MiniBrowser/win/stdafx.cpp: Added.
+ * MiniBrowser/win/stdafx.h: Added.
+
+2010-04-09 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ webkit-patch attached my patch to the wrong bug
+ https://bugs.webkit.org/show_bug.cgi?id=37015
+
+ The problem here is that SVN was violating SCM's implicit
+ contract of always returning paths relative to the repository root.
+ That can easily be fixed by telling SVN that the CWD is the repository root.
+
+ When fixing this I realized there are a large number of places in SCM.py where
+ we want to consider explicitly passing self.checkout_root as the CWD.
+ That would allow scm methods to be executed even when the CWD is not inside
+ the scm tree at all, and would also make sure (in the case of SVN) that paths
+ returned are relative to the root. Git (almost always) returns paths relative
+ to the repository root.
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+ - Explicitly pass self.checkout_root as cwd in run_status_and_extract_filenames
+ - Add a ton of FIXMEs about the need to go back and decide which methods require cwd=self.checkout_root
+ and which do not. We'll probably add a helper function to scm (likely SCM._run) which
+ always passes cwd=self.checkout_root to Executive.run_command
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ - Add a test for this change.
+ * Scripts/webkitpy/tool/commands/upload.py:
+ - Removed the explicit os.chdir to the repository root, since scm.py methods
+ should be robust against the cwd not being equal to the root.
+
+2010-04-09 Adam Roben <aroben@apple.com>
+
+ Don't return 0 as a JSValueRef
+
+ That is an illegal use of the JSC API.
+
+ Fixes <http://webkit.org/b/37333> REGRESSION (r57292): :visited tests
+ are asserting on debug Windows and GTK builds
+
+ Reviewed by Anders Carlsson.
+
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::computedStyleIncludingVisitedInfo):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::computedStyleIncludingVisitedInfo):
+ Return an "undefined" JSValueRef instead of 0.
+
+2010-04-09 Csaba Osztrogonác <ossy@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Make DumpRenderTree parallelizable
+ https://bugs.webkit.org/show_bug.cgi?id=36899
+
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (libraryPathForDumpRenderTree): Use DUMPRENDERTREE_TEMP if exist.
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::DumpRenderTree): Use DUMPRENDERTREE_TEMP if exist.
+ * Scripts/run-webkit-tests:
+ - Create a unique temporary directory and pass its path to
+ DumpRenderTree with DUMPRENDERTREE_TEMP environment variable.
+
+2010-04-09 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by Oliver Hunt.
+
+ <http://webkit.org/b/37326> IDL files are being copied in to the WebCore framework again
+
+ Add a script to detect the presence of inappropriate files in the frameworks. At present
+ it only looks for .css, .idl and .in files in the top level of WebCore.framework/Resources,
+ as these are the only cases we've encountered recently. It can be extended to check the
+ other frameworks or for other inappropriate files in the future.
+
+ * Scripts/check-for-inappropriate-files-in-framework: Added.
+
+2010-04-08 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ Add option to build WebKit2 to build-webkit.
+
+ * Scripts/build-webkit:
+
+2010-04-08 Darin Adler <darin@apple.com>
+
+ Reviewed by Alexey Proskuryakov.
+
+ run-webkit-tests should respect argument order
+ https://bugs.webkit.org/show_bug.cgi?id=37257
+
+ * Scripts/run-webkit-tests: Changed so that sorting is done only
+ on the results of iterating directories. Test order is based on
+ what's passed on the command line. Removed code that aimed to
+ eliminate duplicates since it can be useful to run the same test
+ more than once.
+
+2010-04-07 David Hyatt <hyatt@apple.com>
+
+ Reviewed by Oliver Hunt.
+
+ https://bugs.webkit.org/show_bug.cgi?id=24300, don't expose history info via CSS. Add a new method for
+ obtaining computed style with :visited info included. This allows layout tests to actually tell that
+ :visited is in effect.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (computedStyleIncludingVisitedInfoCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::computedStyleIncludingVisitedInfo):
+
+2010-04-07 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Enable rebaseline-chromium-webkit-tests to run from a webkit-only
+ checkout (i.e., you don't need anything from the Chromium tree checked
+ out). This requires us to introduce the concept of a "target"
+ port/platform that we use to get configuration information from as well
+ as the "running" port that we use to make directories and diff images
+ and the "rebaselining" port we use to actually manage baselines.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37238
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-04-05 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] REGRESSION:(r50665) QWebFrame::setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff) has no effect.
+ https://bugs.webkit.org/show_bug.cgi?id=29431
+
+ Added stubs for Mac, win, gtk and wx DRTs to implement setScrollbarPolicy method.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (setScrollbarPolicyCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setScrollbarPolicy):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::setScrollbarPolicy):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::setScrollbarPolicy):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::setScrollbarPolicy):
+
+2010-04-01 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by David Hyatt.
+
+ [Qt] REGRESSION:(r50665) QWebFrame::setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff) has no effect.
+ https://bugs.webkit.org/show_bug.cgi?id=29431
+
+ Make possible to DRT to set scrollbar policies (on, off or auto).
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setScrollbarPolicy):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ * QtLauncher/main.cpp:
+ (LauncherWindow::toggleScrollbars):
+ (LauncherWindow::createChrome):
+
+2010-04-08 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests should give a percent complete indication
+ https://bugs.webkit.org/show_bug.cgi?id=37258
+
+ Because it's awesome.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-08 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Add back the --target option because it's needed by the downstream
+ Chromium bots.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-08 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ new-run-webkit-tests should understand set-webkit-configuration
+ https://bugs.webkit.org/show_bug.cgi?id=37252
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-08 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Rename target to configuration in new-run-webkit-tests to match the rest of WebKit
+ https://bugs.webkit.org/show_bug.cgi?id=37251
+
+ The rest of our tools call --debug or --release the configuration.
+ It's confusing to call it target in this script.
+
+ * Scripts/webkitpy/layout_tests/driver_test.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py:
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py:
+
+2010-04-07 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Remove depricated op.popen2 call in new-run-webkit-tests
+ https://bugs.webkit.org/show_bug.cgi?id=37249
+
+ Python complains that this API is depricated. We already solved this
+ problem in executive.py.
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-04-07 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests crashes when run on a 64-bit machine
+ https://bugs.webkit.org/show_bug.cgi?id=37248
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ - The code was trying to always run the 32-bit intel version
+ of the DumpRenderTree binary. DRT does not build 32-bit on 64-bit
+ machines so that makes no sense. This may have made sense for test_shell
+ at some point, but I think we should just remove this for DRT.
+
+2010-04-07 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ WebKit Apache configs only listen on IPv4 addresses, causing random timeouts
+ https://bugs.webkit.org/show_bug.cgi?id=37104
+
+ Add warnings that the partial support for specifying what port numbers
+ apache should bind to is even more broken after this fix.
+
+ * Scripts/run-webkit-httpd:
+ * Scripts/run-webkit-tests:
+
+2010-04-07 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by Darin Adler.
+
+ LayoutTestController::m_handlesAuthenticationChallenges isn't initialized
+ https://bugs.webkit.org/show_bug.cgi?id=37190
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (LayoutTestController::LayoutTestController):
+
+2010-03-31 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add webkit-patch command to crawl buildbot history and find when tests
+ started to fail.
+ https://bugs.webkit.org/show_bug.cgi?id=36911
+
+ This is a very bare-bones implementation, which works, but isn't pretty.
+ We will need further re-factoring and improvement to this code, but
+ after long discussions with Adam, I think it's best that we land this
+ and iterate from there.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ - Add revision_build_pairs_with_results for cleaner code and possible
+ optimization of this command.
+ - Return None if a build number can't be found for a revision in
+ build_for_revision
+ - Separate out suspect_revisions_for_transition for re-use by
+ FailureReason
+ - Add LayoutTestResults.failing_tests() and make our parsing code
+ explict about what tables it accepts.
+ * Scripts/webkitpy/tool/commands/queries.py:
+ - Move _print_blame_information_for_commit out of WhatBroke for re-use
+ by FailureReason.
+ - Add FailureReason command which can crawl a given builder and explain
+ why it is currently red on a per-test basis.
+
+2010-04-06 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add DumpRenderTree.gyp, and some small fixes
+ https://bugs.webkit.org/show_bug.cgi?id=37137
+
+ - Add DumpRenderTree.gyp
+ - Remove some dependencies to base/string_util.h,
+ base/compiler_specific.h, base/file_path.h, base/file_util.h,
+ base/message_loop.h, base/sys_string_conversions.h,
+
+ * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp: Added.
+ * DumpRenderTree/chromium/CppVariant.cpp:
+ (CppVariant::toStringVector):
+ * DumpRenderTree/chromium/EventSender.cpp:
+ (EventSender::EventSender):
+ (EventSender::keyDown):
+ (EventSender::scheduleAsynchronousClick):
+ (EventSender::beginDragWithFiles):
+ * DumpRenderTree/chromium/LayoutTestController.cpp:
+ (LayoutTestController::setUserStyleSheetLocation):
+ (LayoutTestController::pathToLocalResource):
+ * DumpRenderTree/chromium/TextInputController.cpp:
+ (TextInputController::markedRange):
+ (TextInputController::selectedRange):
+
+2010-04-06 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Write stack traces into the results directory for new-run-webkit-tests,
+ instead of writing them alongside the test file.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36504
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py:
+
+2010-04-06 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed build fix.
+
+ Enable pixel tests by default in new-run-webkit-tests unless
+ explicitly set by the port or by the command line. This was broken in
+ the fix for bug 36801 (rev. 57173).
+
+ https://bugs.webkit.org/show_bug.cgi?id=37184
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-06 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Disable pixel tests on the mac port by default.
+
+ Also, revamp the way we check for pixel tests being enabled or
+ disabled. We now look for options.pixel_tests instead of
+ options.no_pixel_tests, and we have the "--pixel-tests" (force enable)
+ and "--no-pixel-tests" (force disable) flags.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36801
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations_test.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-04-06 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ Reviewed by Laszlo Gombos.
+
+ [Qt] Added the iPhone and iPad latest user agent in QtLauncher UA switcher
+ https://bugs.webkit.org/show_bug.cgi?id=37159
+
+ * QtLauncher/useragentlist.txt:
+
+2010-04-06 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Add current user-agent string for Symbian for QtLauncher
+ https://bugs.webkit.org/show_bug.cgi?id=37131
+
+ * QtLauncher/useragentlist.txt:
+
+2010-04-06 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Explain how to handle rollout patches
+ https://bugs.webkit.org/show_bug.cgi?id=37139
+
+ We need to set expectations for how long landing rollout patches with
+ the commit-queue takes. The commit-queue is optimized for safety, not
+ performance. Also, give folks an alternative way to land patches
+ quickly.
+
+ In addition, improve our testing of add_patch_to_bug by having
+ MockBugzilla log. This caused me to tighten a bunch of tests and
+ notice that one of our tests wasn't being run.
+
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+
+2010-04-06 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Turns out commit_info.committer() can be None
+ https://bugs.webkit.org/show_bug.cgi?id=37106
+
+ When the committer isn't in committers.py, the committer() property on
+ commit_info can be None. We need to handle that case gracefully.
+
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ * Scripts/webkitpy/tool/bot/sheriff.py:
+ * Scripts/webkitpy/tool/bot/sheriff_unittest.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+
+2010-04-06 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] [Symbian] Build fix for Dumprendertree if Qt printing is not supported
+ https://bugs.webkit.org/show_bug.cgi?id=37082
+
+ Use the QT_NO_PRINTER guard to flag QPrinter dependent code.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::dryRunPrint):
+
+2010-04-05 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ SheriffBot should force_build builders that are idle and have failed exactly once
+ https://bugs.webkit.org/show_bug.cgi?id=37059
+
+ We can get into a deadlocked state where the commit-queue is stopped
+ because the builders are red but the SheriffBot hasn't taken action
+ because the builder has failed only once. The SheriffBot should force
+ build idle builders that have failed exactly once to either turn the
+ tree green again (if the test was flaky) or trigger the "failed twice"
+ remedies (IRC and bug posts).
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/tool/bot/sheriff.py:
+ * Scripts/webkitpy/tool/bot/sheriff_unittest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-05 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Fixed check-webkit-style issue where the script was prematurely
+ exiting when encountering deleted files in patches.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37122
+
+ * Scripts/webkitpy/style/checker.py:
+ - Changed non-existent file message from ERROR to WARN.
+ - StyleChecker.check_file() no longer raises an exception when
+ a file is not found.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated unit tests as necessary.
+
+2010-04-05 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ SheriffBot should include blamelist when posting to bugs
+ https://bugs.webkit.org/show_bug.cgi?id=37113
+
+ When posting on bugs, we should include the full list of SVN revisions
+ that caused the regression to folks have a better sense of whether they
+ are to blame.
+
+ * Scripts/webkitpy/tool/bot/sheriff.py:
+ * Scripts/webkitpy/tool/bot/sheriff_unittest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-05 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed after discussion with Adam, Darin, and Eric.
+
+ Deleted the auto-install directory since it is no longer needed in
+ source control (it is auto-generated).
+
+ Also added webkitpy/thirdparty/autoinstalled to webkitpy/thirdparty's
+ svn:ignore property.
+
+ * Scripts/webkitpy/thirdparty/autoinstalled: Removed.
+
+2010-04-05 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by Adele Peterson.
+
+ Test case for <http://webkit.org/b/37115> / <rdar://problem/7829331>.
+ REGRESSION(r56989): Crash in Mail in WebCore::Position::isCandidate when deleting block using block deletion UI
+
+ Add a JavaScript hook in DRT to call through to WebView's -setEditable:. This is required in order to reproduce
+ the crash.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (setWebViewEditableCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setWebViewEditable):
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetWebViewToConsistentStateBeforeTesting):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::setWebViewEditable):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::setWebViewEditable):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::setWebViewEditable):
+ (LayoutTestController::layerTreeAsText):
+
+2010-04-05 Darin Adler <darin@apple.com>
+
+ Ignore more files the Python tools strew about the working directory.
+
+ * Scripts/webkitpy: Added property svn:ignore.
+ * Scripts/webkitpy/common: Added property svn:ignore.
+ * Scripts/webkitpy/common/checkout: Added property svn:ignore.
+ * Scripts/webkitpy/common/config: Added property svn:ignore.
+ * Scripts/webkitpy/common/net: Added property svn:ignore.
+ * Scripts/webkitpy/common/thread: Added property svn:ignore.
+ * Scripts/webkitpy/python24: Added property svn:ignore.
+ * Scripts/webkitpy/thirdparty/autoinstalled: Modified property svn:ignore.
+ * Scripts/webkitpy/tool/bot: Added property svn:ignore.
+
+2010-04-05 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Add Vitaly Repeshko as a committer.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-05 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Update kenne's IRC nick to his registered nick.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-05 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ assorted helper functions and cleanup of git utilities
+ https://bugs.webkit.org/show_bug.cgi?id=37103
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+ No code currently uses the optional dry_run argument. So removing it.
+ Change all uses of "trunk" to read the correct value out of the git config.
+ Made the dcommit call actually get called when dry_run==true.
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ * Scripts/webkitpy/common/net/credentials.py:
+ Move the git config call into scm.
+ * Scripts/webkitpy/common/net/credentials_unittest.py:
+ * Scripts/webkitpy/common/system/executive.py:
+ If return_exit_code==true, don't error out, just return the exit_code.
+
+2010-04-05 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ prepare-ChangeLog should take a merge-base for which git branch to diff against.
+ https://bugs.webkit.org/show_bug.cgi?id=36394
+
+ * Scripts/prepare-ChangeLog:
+
+2010-04-05 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Removed the PatchStyleErrorHandler class and incorporated its
+ functionality into the DefaultStyleErrorHandler class.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37067
+
+ * Scripts/webkitpy/style/checker.py:
+ - In the StyleChecker class:
+ - Added a line_number parameter to the check_file() method.
+ - Renamed the handle_style_error parameter to
+ mock_handle_style_error to be consistent with the other mock_*
+ parameter names.
+ - Added a mock_check_file parameter to the check_patch() method
+ to facilitate unit testing the changes in this patch.
+ - Rewrote the check_patch() method with the patch-parsing logic
+ taken from the PatchStyleErrorHandler class.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Added a StyleCheckerCheckFileBase class and sub-classed the
+ existing StyleCheckerCheckFileTest class from it.
+ - Added a StyleCheckerCheckPatchTest class to unit-test the
+ rewritten check_patch() method.
+ - Removed the vestigial __main__ code at the bottom of the file.
+ This is left over from when check-webkit-style was implemented
+ as a module and a wrapper module.
+
+ * Scripts/webkitpy/style/error_handlers.py:
+ - Added an optional line_numbers parameter to the
+ DefaultStyleErrorHandler class constructor and adjusted the
+ __call__() method as necessary.
+ - Removed the PatchStyleErrorHandler class.
+
+ * Scripts/webkitpy/style/error_handlers_unittest.py:
+ - Removed the PatchStyleErrorHandlerTest class which unit-tested
+ the PatchStyleErrorHandler class which is being removed in this
+ patch.
+ - Added a test_line_numbers() test method to the
+ DefaultStyleErrorHandlerTest class to test use of the
+ DefaultStyleErrorHandler's new line_numbers attribute.
+
+2010-04-05 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Tighten SheriffBot's flaky test detector
+ https://bugs.webkit.org/show_bug.cgi?id=37063
+
+ Instead of just looking for two sequential red builds, look for two
+ sequential failures of the same test. This should reduce sheriffbot
+ false positive substantially.
+
+ I'm landing this change unreviewed because I've noticed SheriffBot
+ triggering a lot more false positives now that we've expanded the set
+ of core builders. I've tried to take Eric's comments on Bug 37063 into
+ account. I'm happy to iterate on this patch tomorrow once Eric wakes
+ up.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+
+2010-04-04 John Gregg <johnnyg@google.com>
+
+ Unreviewed, add myself to the committers list.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-04 Robert Hogan <robert@webkit.org>
+
+ Unreviewed, add myself to the committers list.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-04 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, adding my IRC nickname to committers.py
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-04 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Refactored check-webkit-style's option-parsing code.
+
+ https://bugs.webkit.org/show_bug.cgi?id=37064
+
+ * Scripts/check-webkit-style:
+ - Moved the "WebKit checkout not found" check from
+ ArgumentParser.parse() to the calling code.
+ - Moved the --git-commit argument validation from the calling
+ code to ArgumentParser.parse().
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated a unit test as necessary.
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Renamed ArgumentParser._exit_with_help() to _parse_error()
+ and made its error_message parameter required.
+ - Removed the found_checkout parameter from ArgumentParser.parse().
+ - Removed the "WebKit checkout not found" check and moved it
+ to the calling code.
+ - Added --git-commit argument checking.
+
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Adjusted the import statements to be fully qualified.
+ - Changed the ArgumentParserTest class to inherit from
+ LoggingTestCase, and updated the class as necessary.
+ - Added a unit-test for the --git-commit validation.
+ - Added unit tests for the --git-diff and --git-since variants
+ of --git-commit.
+
+2010-04-03 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ The check-webkit-style script now logs an ERROR and exits when
+ encountering a file path that does not exist. Previously, it failed
+ silently on such paths.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36957
+
+ * Scripts/webkitpy/common/system/logtesting.py:
+ - Added a FIXME to rename the LoggingTestCase class to
+ LoggingTestCaseBase.
+
+ * Scripts/webkitpy/style/checker.py:
+ - In the StyleChecker.check_file() method:
+ - Added a mock_os_path_exists parameter.
+ - Renamed the process_file parameter to mock_process_file.
+ - Added logic to log an error and exist if the given path does
+ not exist.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Refactored the StyleCheckerCheckFileTest class slightly to
+ inherit from LoggingTestCase.
+ - Added a test method to unit-test the case of a file that
+ does not exist.
+ - Adjusted the other test methods as necessary.
+
+2010-04-03 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add Tiger Bot to the list of "core builders" (builders which block the commit-queue when red)
+ https://bugs.webkit.org/show_bug.cgi?id=33289
+
+ Add Tiger and SnowLeopard Tests to the core builders. This is a bit of
+ an experiment now that the bots are green. Hopefully we can keep them
+ on the list and have the tree stay greener.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Have Eric and Adam watch the SheriffBot
+ https://bugs.webkit.org/show_bug.cgi?id=37054
+
+ * Scripts/webkitpy/tool/bot/sheriff.py:
+ * Scripts/webkitpy/tool/bot/sheriff_unittest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ commit-queue should ignore builders when landing rollouts
+ https://bugs.webkit.org/show_bug.cgi?id=37051
+
+ When we moved the "builders are red" check into the master process, we
+ forgot about rollouts. I thought we had a test covering this case, but
+ looking at the test, it was a bit too loose. I added a new test and
+ introduced some new logging technology into MockTool to make the test
+ tighter.
+
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-04-02 David Levin <levin@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ sherrifbot should ensure that the rollout reason doesn't start with - (and fix webkit-patch upload).
+ https://bugs.webkit.org/show_bug.cgi?id=37030
+
+ * Scripts/webkitpy/tool/bot/sheriff.py: Ensure that the rollout reason doesn't
+ start with -.
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py: A test with a rollout
+ reason which starts with -.
+ * Scripts/webkitpy/tool/steps/createbug.py: Fix webkit-patch upload.
+
+2010-04-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Fixed check-webkit-style to recognize the short form of the
+ --verbose option, as stated in --help.
+
+ * Scripts/check-webkit-style:
+ - Tweaked one line.
+
+2010-04-02 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix for DumpRenderTree after addition of layerTreeAsText.
+
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::layerTreeAsText):
+
+2010-04-02 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Eric Seidel.
+
+ Accept XHTML-MP content type as XHTML content
+ https://bugs.webkit.org/show_bug.cgi?id=34262
+
+ Register xhtmlmp file extension as the new type
+ for XHTML-MP test content.
+
+ * Scripts/run-webkit-tests:
+ * Scripts/webkitpy/layout_tests/layout_package/test_files.py:
+ * Scripts/webkitpy/layout_tests/port/lighttpd.conf:
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ create-rollout doesn't fill out ChangeLog
+ https://bugs.webkit.org/show_bug.cgi?id=37010
+
+ The contract between apply_reverse_diff and PrepareChangeLogForRevert
+ was unclear. I broke filling out the ChangeLog during rollout earlier
+ when I changed apply_reverse_diff to revert the ChangeLogs because
+ PrepareChangeLogForRevert thought that it was supposed to do that.
+ I've now taught PrepareChangeLogsForRevert the new contract.
+
+ It's unclear to me how to test this change because it's essentially an
+ integration issue that requires the file system. At some point we
+ should think about a testing strategy for integration. As the system
+ becomes larger, we're running into more of these issues.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/tool/steps/preparechangelogforrevert.py:
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add cr-win-ews to QueueStatusServer
+ https://bugs.webkit.org/show_bug.cgi?id=37004
+
+ * QueueStatusServer/model/queues.py:
+ * QueueStatusServer/templates/dashboard.html:
+ * QueueStatusServer/templates/statusbubble.html:
+
+2010-04-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Unit-test networktransaction.py's log messages, and add a base
+ class to make unit-testing log messages even easier.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36958
+
+ The purpose of this patch is also to provide a mini-tutorial on
+ how to unit-test Python logging.py messages.
+
+ * Scripts/webkitpy/common/net/networktransaction_unittest.py:
+ - Unit-tested the log messages in test_retry().
+
+ * Scripts/webkitpy/common/system/logtesting.py:
+ - Adjusted the LogTesting class by moving the code that clears
+ the array of log messages into a finally block. This prevents
+ redundant AssertionErrors from getting rendered to the screen
+ while running unit tests.
+ - Added a LoggingTestCase class so the setUp() and tearDown()
+ methods do not need to be implemented in order to test logging.
+ Rather, TestCase classes can simply inherit from this class.
+
+2010-04-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Re-wrote check-webkit-style's argument parsing code to use
+ Python's optparser module and more uniform error-handling logic.
+
+ https://bugs.webkit.org/show_bug.cgi?id=34676
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Removed "option help" from check-webkit-style's usage string
+ since that is provided separately by the OptionParser class.
+ - Also changed the usage string from a function to a constant
+ string _USAGE.
+ - Added an _EPILOG string which renders after OptionParser's
+ usage string and option help.
+ - In the ArgumentParser class:
+ - Changed the constructor's stderr_write parameter to a
+ mock_stderr since the OptionParser accepts a sys.stderr
+ substitute rather than a sys.stderr.write substitute.
+ - Changed the constructor to set a _parser data attribute with
+ an OptionParser instance.
+ - Added a _create_option_parser() method which instantiates
+ the OptionParser.
+ - Updated _exit_with_help() to interact with the OptionParser's
+ help method.
+ - Updated the parse() method as necessary. Also changed the
+ raising of ValueErrors to calls to _exit_with_help().
+
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Removed the CreateUsageTest class since the create_usage method
+ was replaced by a constant string.
+ - Added a _MockStdErr class to the ArgumentParserTest class.
+ - Updated the unit tests as necessary.
+
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ The master commit-queue process should take responsibility for checking that the builders are green
+ https://bugs.webkit.org/show_bug.cgi?id=37009
+
+ We had a failure where the child process noticed that the builders were
+ red. We've always had this race condition, but the new optimistic
+ design made it easier to trigger.
+
+ * Scripts/webkitpy/tool/commands/queues.py:
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Tweak rollout changelog to pass ValidateReviewer check
+ https://bugs.webkit.org/show_bug.cgi?id=37019
+
+ We need to use the magic word "unreviewed" to make the commit-queue
+ happy when landing rollouts.
+
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py:
+
+2010-04-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Removed duplicate ChangeLog entry.
+
+2010-04-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Rubber-stamped by Eric Seidel.
+
+ To the Python 2.4 error message, added a link to the wiki page
+ that contains instructions on how to upgrade.
+
+ * Scripts/test-webkitpy:
+ - Eliminated a use of the ternary operator in configure_logging()
+ to let the version warning display in case of Python 2.4.
+
+ * Scripts/webkitpy/python24/versioning.py:
+ - Added this link to the error text:
+ http://trac.webkit.org/wiki/PythonGuidelines
+
+ * Scripts/webkitpy/python24/versioning_unittest.py:
+ - Updated unit test text.
+
+2010-04-02 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Record the IRC nick of folks who request rollouts
+ https://bugs.webkit.org/show_bug.cgi?id=36999
+
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add cr-win-ews
+ https://bugs.webkit.org/show_bug.cgi?id=36974
+
+ Adds support for an Early Warning System for Chromium on Linux. The
+ interface to the Chromium port is the same on every platform, so we
+ don't need to create a new Port object for this queue.
+
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Using a failure exit code when failing to load a required import
+ https://bugs.webkit.org/show_bug.cgi?id=37000
+
+ Well spotted by Mark Rowe.
+
+ * Scripts/webkitpy/common/system/user.py:
+
+2010-04-01 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Rewrote the revision (r56942) to disable the 79 character line
+ length limit Python/PEP8 style check.
+
+ https://bugs.webkit.org/show_bug.cgi?id=33639#c39
+
+ This rewrite puts the disabling not in the PythonProcessor but
+ in the calling code's default filter rule configuration. This
+ allows the user to check line-length style from the command-line
+ if desired.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added "-pep8/E501" to the _BASE_FILTER_RULES configuration
+ variable to disable the line-length check.
+ - Added "-pep8/E501" to the list of recognized style categories
+ to permit the category to be checked from the command line.
+
+ * Scripts/webkitpy/style/processors/python.py:
+ - Reverted r56942: http://trac.webkit.org/changeset/56942
+
+2010-04-01 Kinuko Yasuda <kinuko@chromium.org>
+
+ Reviewed by Dmitry Titov.
+
+ Add FileThread for async file operation support in FileReader and FileWriter
+ https://bugs.webkit.org/show_bug.cgi?id=36896
+
+ Add options to enable FILE_READER and FILE_WRITER support.
+
+ * Scripts/build-webkit:
+
+2010-04-01 Kent Tamura <tkent@chromium.org>
+
+ Unreviewed. Add missing license header.
+
+ * DumpRenderTree/chromium/TestShellMac.mm:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Improve the error handling in rollout a bit
+ https://bugs.webkit.org/show_bug.cgi?id=36995
+
+ This patch does a few things to make the error handling in rollout a
+ bit more robust.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ The old logic here was wrong. We don't want to resolve the
+ ChangeLogs (that would remove the old ChangeLog entry). Instead,
+ we want to revert the ChangeLogs so we can fill them with the new
+ message.
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ Update test expectations because we're using a different mock object.
+ * Scripts/webkitpy/tool/commands/download.py:
+ - Added an update command to make updating from the SheriffBot more
+ robust.
+ - Now that we have CommitInfo, we can automatically CC the
+ responsible parties on the bug we create.
+ - Re-ordered the steps in create-rollout. Our original thinking
+ was that we always wanted to create the bug, but that's not
+ really true given how things appear to be playing out. If we
+ fail to apply the reverse diff, we don't want to create the bug.
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ - Use the new, more robust update command.
+ * Scripts/webkitpy/tool/steps/createbug.py:
+ Allow commands to pre-load who they want to be CCed on a new bug.
+
+2010-04-01 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add TestShell and WebViewHost class, main(), and so on
+ https://bugs.webkit.org/show_bug.cgi?id=36814
+
+ This change introduces:
+ - WebViewHost class
+ It's an implementation of some delegates required by Chromium
+ WebKit API, and manages painting of a WebView. It's base on
+ src/webkit/tools/test_shell/test_webview_delegate.{cc,h} of
+ Chromium rev.40492.
+ - TestShell class
+ The TestShell instance holds global states of DumpRenderTree process.
+ Unlike TestShell class of Chromium test_shell, TestShell instance is
+ created just once.
+ - DumpRenderTree.cpp
+ The program entry.
+
+ * DumpRenderTree/chromium/DumpRenderTree.cpp: Added.
+ * DumpRenderTree/chromium/TestShell.cpp: Added.
+ * DumpRenderTree/chromium/TestShell.h: Added.
+ * DumpRenderTree/chromium/TestShellMac.mm: Added.
+ * DumpRenderTree/chromium/WebViewHost.cpp: Added.
+ * DumpRenderTree/chromium/WebViewHost.h: Added.
+ * DumpRenderTree/chromium/config.h: Added.
+
+2010-04-01 David Levin <levin@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ sheriffbot rollout should verify that the svn revision is a number.
+ https://bugs.webkit.org/show_bug.cgi?id=37001
+
+ * Scripts/webkitpy/common/net/bugzilla.py: Allow for the message to be None.
+ * Scripts/webkitpy/tool/bot/sheriff.py: Did verification that svn revision
+ is a number. Fixed the imports (since the files uses ScriptError and log)
+ and a typo.
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py: Added a test to verify
+ the behavior.
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Print an error message for readline bogosity in MacPorts
+ https://bugs.webkit.org/show_bug.cgi?id=36979
+
+ * Scripts/webkitpy/common/system/user.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Don't pass --non-interactive to create-rollout
+ https://bugs.webkit.org/show_bug.cgi?id=36989
+
+ It turns out you can't pass --non-interactive to create-rollout. Also,
+ improve our error reporting slighly to catch the case where we error
+ out after creating the rollout bug.
+
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriff.py:
+
+2010-04-01 Ojan Vafai <ojan@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ webkit-patch can incorrectly think the working directory is modified
+ https://bugs.webkit.org/show_bug.cgi?id=36985
+
+ If a file's modification time is modified, but the contents are not,
+ then diff-index will think the file has been modified unless you do
+ some crazy update-index call. Instead, call diff --name-only, which
+ has the index update builtin.
+
+ Tried to write a test, but could not reproduce this in a unittest.
+ To test manually:
+ touch file-in-repo
+ git diff-index HEAD
+ git diff HEAD --name-only
+
+ The diff-index call incorrectly shows file-in-repo as modified.
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add rollout command to sheriffbot
+ https://bugs.webkit.org/show_bug.cgi?id=36986
+
+ This IRC command creates a new bug an attaches a rollout patch. To
+ actually commit the rollout, a committer needs to mark the patch
+ commit-queue+ in bugs.webkit.org.
+
+ Also, factored out some of the logic from the queue into a separate
+ object for easier testing.
+
+ * Scripts/webkitpy/common/system/executive.py:
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/sheriff.py: Added.
+ * Scripts/webkitpy/tool/bot/sheriff_unittest.py: Added.
+ * Scripts/webkitpy/tool/bot/sheriffircbot.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+
+2010-04-01 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, only ignoring chatty style errors.
+
+ check-webkit-style: WebKit needs a python style checker
+ https://bugs.webkit.org/show_bug.cgi?id=33639
+
+ Remove the 79 char line limit by ignoring
+ pep8/E501. Because we have our own report_error
+ implementation we have to ignore E501 by hand
+ instead of passing --ignore=E501.
+
+ Right now over 1400 lines of our existing python
+ fail E501 so this rule just generates needless noise.
+ The rest of WebKit has no wrapping rule so it makes
+ little sense for our Python to differ here.
+
+ * Scripts/webkitpy/style/processors/python.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Add cmarrin's IRC nickname.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ EWS spins hot when unable to build
+ https://bugs.webkit.org/show_bug.cgi?id=36981
+
+ The problem is that the queue engine things we have more work to do,
+ but the bot isn't actually able to do anything. After this change, we
+ back off the usual amount.
+
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/queueengine_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-04-01 Chris Marrin <cmarrin@apple.com>
+
+ Reviewed by Simon Fraser.
+
+ Added layerTreeAsText function to DRT (for Mac)
+ https://bugs.webkit.org/show_bug.cgi?id=36782
+
+ This is the DRT side. It exposes the call to JavaScript
+ through the LayoutTestController.
+
+ * DumpRenderTree/LayoutTestController.cpp:Platform independent JavaScript shim
+ (layerTreeAsTextCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:Mac specific plumbing to WebKit
+ (LayoutTestController::layerTreeAsText):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:Windows specific plumbing to WebKit
+ (LayoutTestController::layerTreeAsText):
+
+2010-04-01 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix after addition of JavaScriptCore/wtf/text directory.
+
+ * wx/build/settings.py:
+
+2010-04-01 Jian Li <jianli@chromium.org>
+
+ Rubber-stamped by David Levin.
+
+ Add myself to the reviewers list.
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Turns out the Chromium Windows bots don't have pdevenv installed.
+
+ * Scripts/webkitdirs.pm:
+
+2010-04-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Let Chromium Windows build with Visual Studio Express
+ https://bugs.webkit.org/show_bug.cgi?id=36919
+
+ This is horrible, horrible copy/paste code, but that seems to be the
+ way of webkitdirs.pm. :(
+
+ Someone needs to go through an cleanup this code, but I don't have the
+ heart to do it in this patch.
+
+ * Scripts/webkitdirs.pm:
+
+2010-04-01 Ilya Tikhonovsky <loislo@chromium.org>
+
+ Reviewed by Yury Semikhatsky.
+
+ Add myself to the committers list.
+ https://bugs.webkit.org/show_bug.cgi?id=36953
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-04-01 Eric Seidel <eric@webkit.org>
+
+ Rubber-stamped by Adam Barth.
+
+ Add Snow Leopard Release bot to the list of "core builders" (builders which stop the commit-queue when they turn red)
+ https://bugs.webkit.org/show_bug.cgi?id=33292
+
+ Just adding the "Build" builder for now.
+ We'll add the "Test" builders when the tests
+ are less flaky.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+
+2010-04-01 Yury Semikhatsky <yurys@chromium.org>
+
+ Reviewed by David Levin.
+
+ Add myself to the reviewers list.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36935
+
+ * Scripts/webkitpy/common/config/committers.py:
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Dave Levin.
+
+ Added Python style checking to check-webkit-style using
+ the third-party pep8 module (via autoinstall).
+
+ https://bugs.webkit.org/show_bug.cgi?id=33639
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added PYTHON to FileType.
+ - Updated ProcessorDispatcher to return a PythonProcessor
+ for *.py files.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated the ProcessorDispatcher unit tests for *.py files.
+
+ * Scripts/webkitpy/style/processors/python.py: Added.
+ - Added PythonProcessor class.
+
+ * Scripts/webkitpy/style/processors/python_unittest.py: Added.
+ - Added PythonProcessor unit tests.
+
+ * Scripts/webkitpy/style/processors/python_unittest_input.py: Added.
+ - Added a sample Python file to test the PythonProcessor.process()
+ code path (since pep8 accepts a file path).
+
+ * Scripts/webkitpy/style/unittests.py:
+ - Updated the style unit test file to import python_unittest.py.
+
+ * Scripts/webkitpy/style_references.py:
+ - Adjusted style references to import pep8.
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Dave Levin.
+
+ Rewrote autoinstall.py to support unzipping *.zip files after
+ download, unzipping and extracting *.tar.gz files after download,
+ and copying installed files to a given destination directory.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35163
+
+ These changes will let us autoinstall pep8.py from the web and put
+ our third-party autoinstalled code in an explicit directory like
+ webkitpy/thirdparty/autoinstalled. These changes should also speed
+ up the execution of autoinstalled *.zip packages slightly since
+ *.pyc files cannot be generated when importing from zipped
+ packages using the current autoinstall.
+
+ * Scripts/test-webkitpy:
+ - Addressed the FIXME to enable auto-install logging once
+ autoinstall was rewritten not to log as verbosely.
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ - Updated ircbot and irclib import statements.
+
+ * Scripts/webkitpy/common/net/networktransaction.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/common/net/networktransaction_unittest.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/common/net/statusserver.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/common/system/autoinstall.py: Added.
+ - Added AutoInstaller class.
+ - Added sample/testing usage to the __main__ block.
+
+ * Scripts/webkitpy/thirdparty/__init__.py:
+ - Updated the autoinstall lines to use the new autoinstall methods.
+ - Added pep8.py to the list of auto-installed packages.
+ - Added a README file to the target autoinstallation directory
+ so users know that the directory is safe to delete.
+
+ * Scripts/webkitpy/thirdparty/autoinstall.py: Removed.
+ - This is replaced by the rewritten autoinstall
+ webkitpy/common/system/autoinstall.py.
+
+ * Scripts/webkitpy/thirdparty/autoinstalled/__init__.py: Removed.
+ - The target autoinstallation directory is now auto-generated.
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Tweak webkitpy's logtesting.LogTesting class to get more mileage out
+ of our unit tests that test log messages.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36886
+
+ This patch adds to the LogTesting class's tearDown() method a line
+ asserting that the array of remaining log messages is empty. This
+ ensures that no extra log messages are getting logged that the caller
+ might not be aware of or may have forgotten to check for.
+
+ * Scripts/webkitpy/common/system/logtesting.py:
+ - Modified the tearDown() method as described above.
+ - Also modified the assertMessages() method to clear the array
+ of log messages after asserting.
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Added support for a --verbose-logging flag to test-webkitpy.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36429
+
+ Verbose logging is useful for debugging test-webkitpy code that
+ runs before the actual unit tests -- things like autoinstalling and
+ unit-test auto-detection logic. This is different from verbose
+ logging of the unit tests themselves (which corresponds to the
+ unittest module's --verbose flag).
+
+ * Scripts/test-webkitpy:
+ - In the configure_logging() method--
+ - Added an is_verbose_logging parameter that sets the logging
+ level to logging.DEBUG instead of logging.INFO.
+ - Changed the method to throttle the logging level on the
+ root logger's handler instead of directly on the root logger
+ itself.
+ - Enabled logging of the autoinstall module when the flag is set.
+
+ * Scripts/webkitpy/thirdparty/autoinstalled/__init__.py:
+ - Added a work-around for a bug in Python 2.6's logging module
+ that was discovered while working on this patch.
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Enabled Python's logging module for webkit-patch, and replaced
+ deprecated_logging with Python logging in networktransaction.py.
+ This eliminates some spurious output when running test-webkitpy.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36725
+
+ * Scripts/webkit-patch:
+ - Replaced the configure_logging() function with a call to
+ the new logutils.configure_logging() function.
+
+ * Scripts/webkitpy/common/net/networktransaction.py:
+ - Replaced the use of deprecated_logging with Python logging.
+
+ * Scripts/webkitpy/common/system/logutils.py:
+ - Added _default_handlers() which creates the default logging
+ handler for webkitpy.
+ - Added configure_logging() which configures default logging
+ for webkitpy.
+
+ * Scripts/webkitpy/common/system/logutils_unittest.py:
+ - Added unit tests for logutils.configure_logging().
+
+ * Scripts/webkitpy/style/checker.py:
+ - Refactored check-webkit-style's configure_logging() method
+ to call the new logutils.configure_logging().
+
+ * Scripts/webkitpy/style_references.py:
+ - Updated references as necessary.
+
+2010-03-31 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ webkit-patch land fails if not run from the root directory
+ https://bugs.webkit.org/show_bug.cgi?id=35822
+
+ The root of the problem was that ChangeLog.__init__ expects a path
+ relative to the current working directory, and SCM expects to
+ return paths relative to the SCM root. Fix it by converting from
+ SCM-relative to absolute paths in Checkout.modified_changelogs
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add more tests for webkitpy.common.checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36926
+
+ We don't have a great way of testing checkout, sadly.
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ REGRESSION: EWS crashes on failure
+ https://bugs.webkit.org/show_bug.cgi?id=36924
+
+ Turns out we need to pass one more argument. My test is kind of lame,
+ but at least it's there.
+
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py:
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Fixed typo in WebKitTools/ChangeLog: opsys -> ospath.
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Make the EWS go faster by being optimistic
+ https://bugs.webkit.org/show_bug.cgi?id=36916
+
+ Have the EWS be optimistic that a patch will correctly build. This
+ should speed up the common case by not requiring two builds for every
+ patch.
+
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add the ability to restart sheriffbot from IRC
+ https://bugs.webkit.org/show_bug.cgi?id=36909
+
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add win-ews to QueueStatusServer
+ https://bugs.webkit.org/show_bug.cgi?id=36876
+
+ The win-ews is still experimental, but it seems to be more or less
+ running. We should show its results to the people.
+
+ * QueueStatusServer/model/queues.py:
+ * QueueStatusServer/templates/dashboard.html:
+ * QueueStatusServer/templates/statusbubble.html:
+
+2010-03-30 Victor Wang <victorw@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Update rebaseline tool to check the release image diff binary and
+ fallback to debug if the release version does not exist.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36245
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-03-31 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Teach buildbot.py how to parse builder activity from /one_box_per_builder
+ https://bugs.webkit.org/show_bug.cgi?id=36898
+
+ I also removed some obsolete FIXMEs and
+ refactored one_box_per_builder parsing into multiple
+ methods for easier reading.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/buildbot_unittest.py:
+
+2010-03-31 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix logging in new-run-webkit-tests so that we don't report IMAGE
+ expected failures as unexpected passes when we run with pixel tests
+ disabled.
+
+ This change splits some of the logic embedded into the TestExpectations
+ classes out into separate pure functions (result_was_expected,
+ remove_image_failures) to make them easier to test. This also adds
+ a parameter to matches_an_expected_result() to indicate whether or
+ not pixel test results should be included in the expectations.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36771
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations_test.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-03-31 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ In webkitpy, refactored two calls to os.path.relpath() replacements
+ to use a common method.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36891
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ - Replaced the relpath implementation with a call to
+ webkitpy.common.system.ospath.relpath().
+
+ * Scripts/webkitpy/common/system/ospath.py: Added.
+ - Moved the relpath() implementation from style/main.py.
+
+ * Scripts/webkitpy/common/system/ospath_unittest.py: Added.
+ - Moved the relpath() unit tests from style/main_unittest.py.
+
+ * Scripts/webkitpy/style/main.py:
+ - Replaced the relpath implementation with a call to
+ webkitpy.common.system.ospath.relpath().
+
+ * Scripts/webkitpy/style/main_unittest.py:
+ - Moved the relpath unit tests to ospath_unittest.py.
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add a Builder.force_build method
+ https://bugs.webkit.org/show_bug.cgi?id=36875
+
+ We plan to eventually use this in SheriffBot to break deadlocks created
+ by flaky tests.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ webkit-patch reads wrong bug url from unified diff context
+ https://bugs.webkit.org/show_bug.cgi?id=36477
+
+ Instead of trying to figure out the bug_id from the diff, we should
+ just get the information from the Checkout object, which understands
+ these concepts.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-03-31 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ webkit-patch commit-queue should land patches optimistically
+ https://bugs.webkit.org/show_bug.cgi?id=34187
+
+ This patch adds an optimistic path to commit-queue and a "fail twice"
+ requirement for rejecting patches. That means we'll land good patches
+ faster (via the optmistic first run) and we'll reject many fewer
+ patches due to flaky tests.
+
+ * Scripts/webkitpy/tool/commands/queues.py:
+
+2010-03-31 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Custom user agent for user agent switcher
+ https://bugs.webkit.org/show_bug.cgi?id=36757
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::showUserAgentDialog):
+
+2010-03-31 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ path to committers.py in commit-queue rejection message is wrong
+ https://bugs.webkit.org/show_bug.cgi?id=36865
+
+ This fix would have only been 3 lines long if we had
+ 1. Had access to an SCM object or tool to give us the checkout root
+ 2. Been able to depend on Python 2.6
+ Instead I've added a bunch of hack code, but at least now
+ we should never have to update this string again as the location
+ of committers.py is fully dynamically discovered. :p
+
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py:
+
+2010-03-31 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Unreviewed test fix for r56809.
+
+ webkit-patch what-broke throws exception
+ https://bugs.webkit.org/show_bug.cgi?id=36852
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+
+2010-03-30 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Rubber stamped by Dave Levin.
+
+ Made check-webkit-style less chatty.
+
+ Examples include:
+ - https://bugs.webkit.org/show_bug.cgi?id=36866#c4
+ - https://bugs.webkit.org/show_bug.cgi?id=36472#c9
+
+ * Scripts/webkitpy/style/checker.py:
+ - Changed unrecognized file type log message from info to debug.
+
+2010-03-30 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix after new method added.
+
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::callShouldCloseOnWebView):
+
+2010-03-30 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ webkit-patch what-broke throws exception
+ https://bugs.webkit.org/show_bug.cgi?id=36852
+
+ * Scripts/webkitpy/common/checkout/api.py: Add missing import StringIO.
+ * Scripts/webkitpy/common/checkout/api_unittest.py: Test the function which previously threw and exception.
+
+2010-03-30 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Darin Adler.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36616
+ Dvorak-Qwerty keyboard layout gives unexpected results in javascript keydown
+
+ https://bugs.webkit.org/show_bug.cgi?id=36797
+ For non-Roman layouts, keydown Event.keyCode is always 0
+
+ * DumpRenderTree/mac/EventSendingController.mm:
+ (-[EventSendingController keyDown:withModifiers:withLocation:]): Generate a correct keyCode
+ for keys used in tests (we used to always pass 0 for 'A').
+
+2010-03-30 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Implement win-ews
+ https://bugs.webkit.org/show_bug.cgi?id=36809
+
+ * Scripts/webkitpy/common/config/ports.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+
+2010-03-30 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Adjusted test-webkitpy to delete all orphaned *.pyc files
+ from webkitpy/ prior to importing any modules from webkitpy.
+ This ensures that no import statements in webkitpy falsely
+ succeed because of leftover *.pyc files.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36599
+
+ * Scripts/test-webkitpy:
+ - Added _clean_pyc_files() to delete orphaned *.pyc files
+ from a directory.
+ - Added _clean_webkitpy_with_test() to call and test
+ _clean_pyc_files().
+ - Moved the "import webkitpy.python24.versioning" statement
+ from the top of the module to the init() method -- immediately
+ after the call to _clean_webkitpy_with_test().
+
+2010-03-30 Fumitoshi Ukai <ukai@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [Chromium-Win] subprocess.call should be called with stdin=open(os.devnull,'r')
+ https://bugs.webkit.org/show_bug.cgi?id=36811
+
+ subproess.Popen() on Python 2.4/Windows with stdout,stdout, but no stdin will fail, because it uses return value of GetStdHandle(STD_INPUT_HANDLE), but DuplicateHandle requires integer, not the handle.
+ We don't need stdin, so use devnull as stdin.
+ Same as https://bugs.webkit.org/show_bug.cgi?id=36586
+
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py: open os.devnull for stdin
+
+2010-03-29 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, build fix only.
+
+ * Scripts/webkitpy/tool/steps/validatereviewer.py: Add missing import.
+
+2010-03-29 Hayato Ito <hayato@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Remove '_flymake' suffix from base part of file name so that
+ check-webkit-style uses a correct header guard name when it is called from Emacs's flymake.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36573
+
+ * Scripts/webkitpy/style/processors/cpp.py:
+ * Scripts/webkitpy/style/processors/cpp_unittest.py:
+
+2010-03-29 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Get the following test-webkitpy unit test working again:
+ scm_unittest.SVNTest.test_svn_apply().
+
+ https://bugs.webkit.org/show_bug.cgi?id=36696
+
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ - Add leading spaces to the empty lines of the ChangeLog strings.
+ - Manually set the _reviewer attribute on the Attachment object
+ to get the tests to pass.
+
+2010-03-29 Martin Robinson <mrobinson@webkit.org>
+
+ Reviewed by Holger Freyther.
+
+ [GTK] suppress (un)desired launcher output that can make layout test to fail with stderr
+ https://bugs.webkit.org/show_bug.cgi?id=36390
+
+ Suppress debugging messages sent to the GLib logger during DRT runs.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (logHandler): Added.
+ (main): Use logHandler as the default GLib log message handler.
+
+2010-03-29 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ sheriff-bot should comment on bugs when builders break
+ https://bugs.webkit.org/show_bug.cgi?id=36786
+
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ - Add a new _post_blame_comment_to_bug and all it from process_work_item
+ - Move commit-queue logic into _post_rollout_patch to make its api match the other _post commands.
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ - Test the new _post_blame_comment_to_bug call
+
+2010-03-29 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ sheriff-bot fails to get information about certain builds
+ https://bugs.webkit.org/show_bug.cgi?id=36768
+
+ This seems to be caused by:
+ http://buildbot.net/trac/ticket/753
+ I have no work-around, but for now at least we're logging
+ the error better. I also added allow_none to our ServerProxy
+ creation in case that fixes things for other versions of python.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+
+2010-03-29 Victor Wang <victorw@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Add sign in/out link to TestResults appengine
+
+ Add a link to main menu for sign in/out to this appengine.
+ People signed in with admin privilege could perform
+ actions that are only allowed to admins like deleting files.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36767
+
+ * TestResultServer/handlers/menu.py:
+ * TestResultServer/stylesheets/menu.css:
+ (.sign):
+ * TestResultServer/templates/menu.html:
+
+2010-03-29 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ ValidateReviewer step is draconian and un-tested
+ https://bugs.webkit.org/show_bug.cgi?id=36792
+
+ ValidateReviewer logic was commented out in
+ http://trac.webkit.org/changeset/56744
+ That was a symptom of the fact that validatereviewer.py
+ is too inflexible to be used when real humans are driving webkit-patch.
+ For now we just disable ValidateReviewer when humans are at the keyboard.
+
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+ - Only run when in non-interactive mode.
+ * Scripts/webkitpy/tool/steps/validatereviewer_unittest.py: Added.
+ - Test our validation logic to make sure it's sane.
+
+2010-03-29 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Eliminate explicit slash characters from check-webkit-style's
+ _rel_path() method to make its implementation more platform
+ independent.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36759
+
+ * Scripts/webkitpy/style/main.py:
+ - Changed to use os.sep instead of slash_chars "/\\". This can
+ be done since os.path.abspath() converts slashes to os.sep.
+
+2010-03-29 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Change new-run-webkit-tests to not use more than four threads by
+ default on the mac port until
+ https://bugs.webkit.org/show_bug.cgi?id=36622 is fixed.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36687
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-29 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Reformat port/mac.py to fit witin 80 columns for PEP-8 compliance.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36691
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-29 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests fails java/lc3 on a clean checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36078
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ - Build the java support files in check_build
+ - Unwrap a line which would still fit under 80col
+
+2010-02-26 Kenneth Rohde Christiansen <kenneth@webkit.org>
+
+ Reviewed by Simon Fraser.
+
+ Add support for Widgets 1.0: View Mode Media Feature
+ https://bugs.webkit.org/show_bug.cgi?id=35446
+
+ Add hooks to the Qt DRT for testing the view mode media feature.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setViewModeMediaFeature):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-03-29 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Update expected results for unit tests.
+
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+
+2010-03-29 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. I think Eric meant svn_revision.
+
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-03-26 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ webkit-patch commit-queue should notice if it breaks builders (and roll out its own changes)
+ https://bugs.webkit.org/show_bug.cgi?id=29311
+
+ Now that we have sheriff-bot watching the tree, it can post
+ rollout patches on behalf of the commit queue.
+
+ * Scripts/webkitpy/common/checkout/commitinfo.py: add responsible_parties()
+ * Scripts/webkitpy/common/checkout/commitinfo_unittest.py: test responsible_parties()
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ - Break IRC logic out into _post_irc_warning for easier testing.
+ - Add _post_rollout_patch for posting rollout patches to bugzilla.
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ - Test _rollout_reason
+ * Scripts/webkitpy/tool/grammar.py:
+ - Fix join_with_separators to not add Adam's "oxford comma" for two item lists.
+ * Scripts/webkitpy/tool/grammar_unittest.py:
+ - Test join_with_separators
+
+2010-03-29 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Sheriffbot should actually run commands
+ https://bugs.webkit.org/show_bug.cgi?id=36776
+
+ Some minor changes to Sheriffbot:
+
+ 1) We should actually run commands (by giving control back to the
+ command processing object.
+
+ 2) Use URLs instead of just numbers to represent SVN revisions (making
+ it easier to folks in IRC ot followup).
+
+ * Scripts/webkitpy/tool/bot/irc_command.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+
+2010-03-26 Kenneth Rohde Christiansen <kenneth@webkit.org>
+
+ Reviewed by Antti Koivisto.
+
+ Renaming of frame flattening LayoutTestController method
+ to setFrameFlatteningEnabled(bool)
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (setFrameFlatteningEnabledCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setFrameFlatteningEnabled):
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::setFrameFlatteningEnabled):
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::resetSettings):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setFrameFlatteningEnabled):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ * DumpRenderTree/win/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::setFrameFlatteningEnabled):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::setFrameFlatteningEnabled):
+
+2010-03-28 Gustavo Noronha Silva <gns@gnome.org>
+
+ No review, rolling out r56679.
+ http://trac.webkit.org/changeset/56679
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ Lots of tests broken.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (createWebView):
+
+2010-03-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Fixed typo in deprecated_logging_unittest.py, which is masking
+ sys.stderr while running test-webkitpy.
+
+ See also-- https://bugs.webkit.org/show_bug.cgi?id=36725#c3
+
+ * Scripts/webkitpy/common/system/deprecated_logging_unittest.py:
+
+2010-03-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Renamed check-webkit-style's --debug flag to --verbose to be more
+ in line with other WebKit scripts. Also renamed the current
+ --verbose flag to --min-confidence to allow the --debug rename.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36521
+
+ We also renamed the internal "verbose" variables to "confidence" or
+ "min_confidence," as appropriate, to avoid confusion with the
+ --verbose flag, and because the new names are more accurate.
+
+ * Scripts/check-webkit-style:
+ - Renamed is_debug to is_verbose.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Renamed _DEFAULT_VERBOSITY to _DEFAULT_CONFIDENCE.
+ - Renamed "verbosity" parameters to "min_confidence" throughout.
+ - Renamed configure_logging()'s is_debug parameter to is_verbose.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated the unit tests as necessary.
+
+ * Scripts/webkitpy/style/error_handlers.py:
+ - Updated the call to StyleCheckerConfiguration.write_style_error().
+
+ * Scripts/webkitpy/style/error_handlers_unittest.py:
+ - Updated the unit tests as necessary.
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Updated the usage string with the new flag names.
+ - Renamed the verbosity parameter to min_confidence throughout.
+ - Renamed the is_debug parameter to is_verbose throughout.
+
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Updated the unit tests as necessary.
+
+ * Scripts/webkitpy/style/processors/cpp.py:
+ - Renamed the verbosity parameter to min_confidence throughout.
+
+ * Scripts/webkitpy/style/processors/cpp_unittest.py:
+ - Updated the unit tests as necessary.
+
+2010-03-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Added back a line that accidentally got deleted in r56690.
+
+ * Scripts/check-webkit-style:
+
+2010-03-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Add to check-webkit-style support for checking directories.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35234
+
+ * Scripts/check-webkit-style:
+ - Replaced the call to check_file() with a call to check_paths().
+
+ * Scripts/webkitpy/style/checker.py:
+ - In the StyleChecker class:
+ - Added a check_paths() method that accepts a list of paths
+ to files and directories.
+ - Added a _check_directory() method that checks the files
+ in a directory.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Added a StyleCheckerCheckPathsTest to unit-test the new
+ check_paths() method.
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Updated the usage string.
+
+2010-03-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Changed check-webkit-style to convert paths to paths relative to
+ the checkout root when invoking check-webkit-style with path
+ arguments. Also added warning messages where appropriate.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35498
+
+ Converting paths to paths relative to the checkout root ensures
+ that style checking will behave as expected (since path-specific
+ rules assume input paths are relative to a source root).
+
+ * Scripts/check-webkit-style:
+ - Added debug logging of whether the current directory was found
+ to be in a WebKit checkout.
+ - Added the found_checkout parameter to the call to parser.parse().
+ - Renamed the files variable to paths.
+ - Added a call to change_directory() prior to checking style.
+
+ * Scripts/webkitpy/style/checker.py:
+ - For StyleChecker.check_file():
+ - Updated the docstring.
+ - Added two log messages.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated a call to parser.parse() with the found_checkout parameter.
+
+ * Scripts/webkitpy/style/main.py: Added.
+ - Added a new file so the code in this patch could be unit-tested,
+ as opposed to adding new code to check-webkit-style.
+ - Added the method _rel_path() as a substitute for os.path.relpath(),
+ which is available only in Python 2.6.
+ - Added the method change_directory(), which contains most of the
+ new functionality in this patch.
+
+ * Scripts/webkitpy/style/main_unittest.py: Added.
+ - Added RelPathTest to test main._rel_path().
+ - Added ChangeDirectoryTest to test main.change_directory().
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Updated check-webkit-style's usage string.
+ - For the ArgumentParser.parse() method:
+ - Added a found_checkout parameter.
+ - Renamed filenames to paths.
+ - Added logic so that an error is raised if no paths are passed
+ if found_checkout is False.
+
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Updated the ArgumentParser.parse() unit tests to include
+ coverage for the new found_checkout parameter.
+
+ * Scripts/webkitpy/style/unittests.py:
+ - Added an import statement for main_unittest.
+
+ * Scripts/webkitpy/style_references.py:
+ - Renamed SimpleScm to WebKitCheckout.
+ - Added a detect_checkout() function to allow returning None
+ instead of a WebKitCheckout instance if no checkout is found.
+ - Renamed checkout_root to root_path.
+
+2010-03-27 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Eric Seidel.
+
+ Print didHandleOnloadEventsForFrame in the callback of
+ onload-event signal comming from frame loader
+
+ [GTK] Improve reporting of frame loader callbacks in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewOnloadEvent):
+ (createWebView): added connection to signal::onload-event and
+ signal callback
+
+2010-03-27 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Eric Seidel.
+
+ Print didCommitLoadForFrame in the callback of signal::load-committed
+
+ [GTK] Improve reporting of frame loader callbacks in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewLoadCommitted):
+ (createWebView): added connection to sinal::load-committed and
+ signal callback
+
+2010-03-27 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Eric Seidel.
+
+ Print didStartProvisionalLoadForFrame in the callback of
+ notify::load-status property change notification
+
+ [GTK] Improve reporting of frame loader callbacks in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webInspectorCloseWindow):
+ (webInspectorInspectWebView):
+ (createWebView): added connection to notify::load-status and
+ signal callback
+
+2010-03-27 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In webkitpy/, deleted the /unittests.py files since test-webkitpy
+ now auto-detects all *_unittest.py files.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36712
+
+ * Scripts/webkitpy/common/net/irc/unittests.py: Removed.
+ * Scripts/webkitpy/common/system/unittests.py: Removed.
+ * Scripts/webkitpy/common/thread/unittests.py: Removed.
+ * Scripts/webkitpy/common/unittests.py: Removed.
+ * Scripts/webkitpy/python24/unittests.py: Removed.
+ * Scripts/webkitpy/style/unittests.py: Removed.
+ * Scripts/webkitpy/tool/unittests.py: Removed.
+ * Scripts/webkitpy/unittests.py: Removed.
+
+2010-03-27 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Eric Seidel.
+
+ Add a CR after printing didFinishDocumentLoadForFrame
+
+ [GTK] Improve reporting of frame loader callbacks in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewLoadFinished):
+
+2010-03-27 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Rubber-stamped by Adam Barth.
+
+ Fixed the name of a unit test file in webkitpy.
+
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py: Copied from WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py.
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py: Removed.
+
+2010-03-26 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ The test-webkitpy script now automatically detects all unit-test
+ files in webkitpy/. This lets us eliminate the need to have and
+ maintain all of the unittests.py files.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36591
+
+ * Scripts/test-webkitpy:
+ - Replaced the "from webkitpy.unittests import *" with a call
+ to webkitpy.test.main.Tester().run_tests().
+
+ * Scripts/webkitpy/test/__init__.py: Copied from WebKitTools/QueueStatusServer/filters/__init__.py.
+ - Required file for the new webkitpy/test/ directory.
+
+ * Scripts/webkitpy/test/main.py: Added.
+ - Added a Tester class that contains the following methods:
+ - _find_unittest_files() to detect all the unit-test files.
+ - _modules_from_paths() to convert the paths to the unit-test
+ files to fully-qualified module names.
+ - run_tests() which calls the above two methods and then passes
+ the module names to Python's unittest module.
+
+2010-03-27 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Simon Fraser.
+
+ [Qt/Win] Add support to unix and windows NS plugin for executing scripts on setWindow.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36701
+
+ * DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp:
+ (webkit_test_plugin_new_instance): Add onSetWindow.
+ (webkit_test_plugin_destroy_instance): Add onSetWindow.
+ (webkit_test_plugin_set_window): Add onSetWindow.
+ * DumpRenderTree/win/TestNetscapePlugin/main.cpp:
+ (NPP_New): Add onSetWindow.
+ (NPP_Destroy): Add onSetWindow.
+ (NPP_SetWindow): Add onSetWindow.
+
+2010-03-26 Robert Hogan <robert@roberthogan.net>
+
+ Reviewed by Simon Hausmann.
+
+ Allow plugins implemented by the application, such as mimetype 'x-qt-plugin',
+ when pluginsEnabled is false.
+
+ Add support for LayoutTestController.WebKitPluginsEnabled
+
+ https://bugs.webkit.org/show_bug.cgi?id=32196
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (copyWebSettingKey):
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::resetSettings):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::overridePreference):
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add some basic IRC commands to sheriffbot
+ https://bugs.webkit.org/show_bug.cgi?id=36684
+
+ Adds support for sheriffbot to respond to a "hi" command and a
+ "last-green-revision" command. It's lame that we're rebuilding
+ MultiCommandTool, but as discussed in person we'll intergrate the two
+ once we see what the requirements are.
+
+ * Scripts/webkitpy/tool/bot/irc_command.py: Added.
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/queueengine_unittest.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py: Added.
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/unittests.py:
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. More errors in the IRC module. I have no idea how to test
+ ircbot.py, which is too bad. Hopefully we've abstracted it away enough
+ that we don't have to touch it very much after this patch.
+
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Add a missing "_".
+
+ * Scripts/webkitpy/common/net/irc/ircproxy.py:
+ * Scripts/webkitpy/common/net/irc/ircproxy_unittest.py: Added.
+ * Scripts/webkitpy/common/net/irc/unittests.py:
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Fix a the commit queue after my recent change.
+
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+
+2010-03-26 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed fix.
+
+ Fix the return value for port/base.diff_image (changed from 1/0 to
+ True/False in bug 34826.
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+
+2010-03-26 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Change the Mac port of new-run-webkit-tests to look for a
+ test_expectations.txt file in addition to the Skipped files, so we
+ can track pixel failures.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36619
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-26 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Implement pixel tests (image diff) properly on the Mac port.
+
+ This change introduces a new "ServerPocess" class that can be used
+ to manage processes that the run-webkit-tests harness forks off and
+ expects to stay up for longer than a single request/response session.
+ Both DumpRenderTree and ImageDiff use this style of communication,
+ although the current code forks off a new ImageDiff for each diff
+ (We need to restructure other parts of the code to be able to do this
+ safely in a multi-threaded environment).
+
+ Also, now that the ServerProcess abstraction exists, we can probably
+ clean up and simplify some of the thread management logic in
+ test_shell_thread as well.
+
+ https://bugs.webkit.org/show_bug.cgi?id=34826
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/server_process.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+
+2010-03-26 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Eric Seidel.
+
+ Print didFinishLoadForFrame outcome in DRT
+
+ [GTK] Improve reporting of frame loader callbacks in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=36454
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewLoadCommitted):
+
+2010-03-26 Eric Seidel <eric@webkit.org>
+
+ Unreviewed test fix.
+ My change conflicted with one of Adam's causing test-webkitpy to fail.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ * Scripts/webkitpy/common/checkout/api_unittest.py:
+ - modified_changelogs is now on Checkout instead of scm.
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Refactor IRCBot controller logic to allow for commands
+ https://bugs.webkit.org/show_bug.cgi?id=36676
+
+ We need to move the controller logic into the tool package so it can
+ know about commands. The changes to queueengine could go in a
+ different patch, but we're going to need it anyway.
+
+ * Scripts/webkitpy/common/config/irc.py: Added.
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ * Scripts/webkitpy/common/net/irc/ircproxy.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/sheriffircbot.py: Added.
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Rubber stamped by Eric Seidel.
+
+ Move the threading code into its own module. (It has nothing to do with IRC.)
+
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ * Scripts/webkitpy/common/net/irc/ircproxy.py:
+ * Scripts/webkitpy/common/net/irc/messagepump.py: Removed.
+ * Scripts/webkitpy/common/net/irc/messagepump_unittest.py: Removed.
+ * Scripts/webkitpy/common/net/irc/threadedmessagequeue.py: Removed.
+ * Scripts/webkitpy/common/net/irc/threadedmessagequeue_unittest.py: Removed.
+ * Scripts/webkitpy/common/net/irc/unittests.py:
+ * Scripts/webkitpy/common/thread: Added.
+ * Scripts/webkitpy/common/thread/__init__.py: Added.
+ * Scripts/webkitpy/common/thread/messagepump.py: Copied from Scripts/webkitpy/common/net/irc/messagepump.py.
+ * Scripts/webkitpy/common/thread/messagepump_unittest.py: Copied from Scripts/webkitpy/common/net/irc/messagepump_unittest.py.
+ * Scripts/webkitpy/common/thread/threadedmessagequeue.py: Copied from Scripts/webkitpy/common/net/irc/threadedmessagequeue.py.
+ * Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py: Copied from Scripts/webkitpy/common/net/irc/threadedmessagequeue_unittest.py.
+ * Scripts/webkitpy/common/thread/unittests.py: Added.
+ * Scripts/webkitpy/common/unittests.py:
+
+2010-03-26 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ Finally add some basic unit testing for Checkout.commit_message_for_this_commit
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/api_unittest.py: Added.
+ * Scripts/webkitpy/common/unittests.py:
+
+2010-03-26 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ [Qt] User Agent Switcher on QtLauncher
+ https://bugs.webkit.org/show_bug.cgi?id=36451
+
+ Patch by Diego Gonzalez <diego.gonzalez@openbossa.org> on 2010-03-26
+ Reviewed by Simon Hausmann.
+
+ Make possible to change the QtLauncher user agent via
+ a predefined list.
+
+ * QtLauncher/QtLauncher.pro:
+ * QtLauncher/QtLauncher.qrc: Added.
+ * QtLauncher/main.cpp:
+ (LauncherWindow::showUserAgentDialog):
+ (LauncherWindow::createChrome):
+ * QtLauncher/useragentlist.txt: Added.
+ * QtLauncher/webpage.cpp:
+ (WebPage::userAgentForUrl):
+ * QtLauncher/webpage.h:
+ (WebPage::setUserAgent):
+
+2010-03-26 Victor Wang <victorw@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Host layout test flakiness dashboard in TestResultServer appengine.
+
+ Flakiness dashboard is a tool to monitor layout test status and
+ help layout test regression diagnostics.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36195
+
+ * TestResultServer/handlers/dashboardhandler.py: Added.
+ - New handler to handle dashboard request
+ * TestResultServer/handlers/menu.py:
+ - Add new dashboard links
+ * TestResultServer/handlers/testfilehandler.py:
+ - Request routes refactory
+ * TestResultServer/index.yaml:
+ * TestResultServer/main.py:
+ - Add new dashboard request routes and refactor test result file rountes.
+ * TestResultServer/model/dashboardfile.py: Added.
+ - Model to access datastore for dashboard files
+ * TestResultServer/model/testfile.py:
+ * TestResultServer/stylesheets/dashboardfile.css: Added.
+ * TestResultServer/templates/dashboardfilelist.html: Added.
+ * TestResultServer/templates/showfilelist.html:
+
+2010-03-26 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Remove some evil statics from CommitInfo
+ https://bugs.webkit.org/show_bug.cgi?id=36637
+
+ These methods should really be on checkout. You can tell because they
+ know about ChangeLogs and take an SCM as an argument. :)
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/commitinfo.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+
+2010-03-23 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ Add an option to QtLauncher to enable/disable a QGLWidget as Viewport
+ of the QGraphicsView when the launcher is running on graphicsview mode.
+
+ [Qt] QtLauncher needs an option to Enable/Disable a QGLWidget as Viewport
+ https://bugs.webkit.org/show_bug.cgi?id=36270
+
+ * QtLauncher/QtLauncher.pro:
+ * QtLauncher/main.cpp:
+ (LauncherWindow::toggleQGLWidgetViewport):
+ (LauncherWindow::createChrome):
+
+2010-03-26 Fumitoshi Ukai <ukai@chromium.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [Chromium-Win] websocket_server.py failed to start
+ https://bugs.webkit.org/show_bug.cgi?id=36586
+
+ subproess.Popen() on Python 2.4/Windows with stdout,stdout, but no stdin will fail, because it uses return value of GetStdHandle(STD_INPUT_HANDLE), but DuplicateHandle requires integer, not the handle.
+ We don't need stdin, so use devnull as stdin.
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py: open os.devnull for stdin
+
+2010-03-26 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add TestNavigationController and TestWebWorker
+ https://bugs.webkit.org/show_bug.cgi?id=36520
+
+ Add LayoutTestController class, which is going to be used by
+ DumpRenderTree Chromium port. These files are based on:
+ - src/webkit/tools/test_shell/layout_test_controller.cc
+ - src/webkit/tools/test_shell/layout_test_controller.h
+ of Chromium rev.40492.
+
+ * DumpRenderTree/chromium/LayoutTestController.cpp: Added.
+ * DumpRenderTree/chromium/LayoutTestController.h: Added.
+
+2010-03-25 Charlie Reis <creis@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ onbeforeunload not called at window close + frame or iframe focused
+ https://bugs.webkit.org/show_bug.cgi?id=27481
+
+ Adds a callShouldCloseOnWebView method to LayoutTestController,
+ to allow automated testing for bug 27481.
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (callShouldCloseOnWebViewCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::callShouldCloseOnWebView):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::callShouldCloseOnWebView):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::callShouldCloseOnWebView):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::callShouldCloseOnWebView):
+
+2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add webkit-patch lkgr for finding last known good revision
+ https://bugs.webkit.org/show_bug.cgi?id=36626
+
+ This is rather slow for now because the command
+ has to compute this information from the buildbot.
+ A better long-term solution would be to have a server
+ somewhere store a pre-computed LKGR and then any
+ script (like webkit-patch) could just fetch it.
+
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move modified_changelogs (and friends) from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36636
+
+ These functions know about ChangeLogs, which is forbidden knowledge in
+ scm.py.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ * Scripts/webkitpy/tool/steps/preparechangelogforrevert.py:
+ * Scripts/webkitpy/tool/steps/revertrevision.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move apply_patch from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36635
+
+ SCM shouldn't have any knowledge of WebKit scripts.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/applypatch.py:
+
+2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Unreviewed build fix to un-break webkit-patch land.
+ Test-case coming in follow-up commit.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
+
+2010-03-25 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ <rdar://problem/7728903> Support color bitmap fonts
+
+ * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj: Added ColorBits.ttf
+ and ColorBits-A.png.
+ * DumpRenderTree/fonts/ColorBits-A.png: Copied from WebCore/inspector/front-end/Images/successGreenDot.png.
+ * DumpRenderTree/fonts/ColorBits.ttf: Added.
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (activateFonts): Activate ColorBits.ttf.
+
+2010-03-25 Mark Rowe <mrowe@apple.com>
+
+ Remove a printf that was causing commit-log-editor to spew the name of the editor
+ to the terminal many times during a commit.
+
+ * Scripts/commit-log-editor:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ This function requires knowledge of ChangeLogs, but scm shouldn't know
+ about ChangeLogs.
+
+ * Scripts/webkitpy/common/checkout/api.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py:
+ * Scripts/webkitpy/tool/steps/commit.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ REGRESSION: webkit-patch land can't land "rubber-stamped" patches
+ https://bugs.webkit.org/show_bug.cgi?id=36582
+
+ Allow a "-" in rubber stamped.
+
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Fix some copyright lines to remove extraneous comma and
+ python directive.
+
+ * Scripts/webkitpy/tool/bot/patchcollection.py:
+ * Scripts/webkitpy/tool/bot/patchcollection_unittest.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/queueengine_unittest.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/comments.py:
+ * Scripts/webkitpy/tool/grammar.py:
+ * Scripts/webkitpy/tool/multicommandtool.py:
+ * Scripts/webkitpy/tool/multicommandtool_unittest.py:
+
+2010-03-25 Simon Fraser <simon.fraser@apple.com>
+
+ Reviewed by Darin Adler, Alexey Proskuryakov.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36631
+ Allow the test plugin to run scripts in response to NPP_SetWindow calls
+
+ Hook up the ability for the TestNetscapePlugIn to run JavaScript in
+ response to NPP_SetWindow.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (pluginAllocate):
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h:
+ * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp:
+ (NPP_New):
+ (NPP_Destroy):
+ (NPP_SetWindow):
+
+2010-03-25 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Unreviewed, build fix.
+
+ [Qt] Fix QtLauncher guards.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::showFPS):
+ (LauncherWindow::updateFPS):
+
+2010-03-25 Yury Semikhatsky <yurys@chromium.org>
+
+ Reviewed by Pavel Feldman.
+
+ Allow running tests with enabled developer extras and closed Web Inspector. Tests that have inspector-enabled/ in their path/url will have developer extras enabled.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36610
+
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (shouldEnableDeveloperExtras):
+ (runTest):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setJavaScriptProfilingEnabled):
+ (LayoutTestController::setDeveloperExtrasEnabled):
+ (LayoutTestController::showWebInspector):
+ (LayoutTestController::closeWebInspector):
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (shouldEnableDeveloperExtras):
+ (runTest):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::setJavaScriptProfilingEnabled):
+ (LayoutTestController::setDeveloperExtrasEnabled):
+ (LayoutTestController::showWebInspector):
+ (LayoutTestController::closeWebInspector):
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::shouldEnableDeveloperExtras):
+ (WebCore::DumpRenderTree::open):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::closeWebInspector):
+ (LayoutTestController::setDeveloperExtrasEnabled):
+ (LayoutTestController::showWebInspector):
+ (LayoutTestController::setJavaScriptProfilingEnabled):
+ * DumpRenderTree/win/DumpRenderTree.cpp:
+ (shouldEnableDeveloperExtras):
+ (runTest):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::setJavaScriptProfilingEnabled):
+ (LayoutTestController::setDeveloperExtrasEnabled):
+ (LayoutTestController::showWebInspector):
+ (LayoutTestController::closeWebInspector):
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Rubber stamped by Eric Seidel.
+
+ Tweaks to sheriffbot to improve latency by keeping the working copy up
+ to date even when there's no build break. Also, officially move
+ sheriffbot to #webkit.
+
+ (Also teach ValidateReviewer to understand rubber stamps.)
+
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+
+2010-03-22 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ Not displaying FPS info on the terminal. On S60 and Maemo the
+ Window title will be used and Status bar will used on desktop.
+
+ [Qt] QtLauncher's FPS info should not be displayed on the terminal
+ https://bugs.webkit.org/show_bug.cgi?id=36244
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::initializeView):
+ (LauncherWindow::showFPS):
+ (LauncherWindow::updateFPS):
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::setFrameRateMeasurementEnabled):
+ (WebViewGraphicsBased::updateFrameRate):
+ * QtLauncher/webview.h:
+ (WebViewGraphicsBased::frameRateMeasurementEnabled):
+
+2010-03-25 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ Reviewed by Kenneth Christiansen.
+
+ Added missing frame flattening activation on
+ fast/frames/flattening/frameset-flattening-grid.html;
+ removed unnecessary CONSOLE MESSAGE from the expected file;
+ reset the setFrameSetFlatteningEnabled for each test.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::resetSettings):
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved deprecated_logging unit test import statement from
+ webkitpy/unittests.py to webkitpy/common/system/unittests.py.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/webkitpy/common/system/unittests.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved webkit_logging.py to common/system/deprecated_logging.py
+ inside webkitpy.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/validate-committer-lists:
+ * Scripts/webkitpy/common/checkout/changelog.py:
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/buildbot.py:
+ * Scripts/webkitpy/common/net/credentials.py:
+ * Scripts/webkitpy/common/net/irc/ircproxy.py:
+ * Scripts/webkitpy/common/net/networktransaction.py:
+ * Scripts/webkitpy/common/net/statusserver.py:
+ * Scripts/webkitpy/common/system/deprecated_logging.py: Copied from WebKitTools/Scripts/webkitpy/webkit_logging.py.
+ * Scripts/webkitpy/common/system/deprecated_logging_unittest.py: Copied from WebKitTools/Scripts/webkitpy/webkit_logging_unittest.py.
+ * Scripts/webkitpy/common/system/executive.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py:
+ * Scripts/webkitpy/tool/commands/openbugs.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/commands/stepsequence.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/multicommandtool.py:
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ * Scripts/webkitpy/tool/steps/applypatch.py:
+ * Scripts/webkitpy/tool/steps/build.py:
+ * Scripts/webkitpy/tool/steps/checkstyle.py:
+ * Scripts/webkitpy/tool/steps/closebug.py:
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff.py:
+ * Scripts/webkitpy/tool/steps/confirmdiff.py:
+ * Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py:
+ * Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py:
+ * Scripts/webkitpy/tool/steps/obsoletepatches.py:
+ * Scripts/webkitpy/tool/steps/preparechangelog.py:
+ * Scripts/webkitpy/tool/steps/reopenbugafterrollout.py:
+ * Scripts/webkitpy/tool/steps/runtests.py:
+ * Scripts/webkitpy/tool/steps/update.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+ * Scripts/webkitpy/unittests.py:
+ * Scripts/webkitpy/webkit_logging.py: Removed.
+ * Scripts/webkitpy/webkit_logging_unittest.py: Removed.
+
+2010-03-25 Julien Chaffraix <jchaffraix@webkit.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ commit-log-editor can call itself in an infinite loop
+ https://bugs.webkit.org/show_bug.cgi?id=35291
+
+ if $editor ends up being commit-log-editor, the script will exec itself
+ in an infinite loop.
+
+ To avoid this, we now check that the $editor variable is not
+ commit-log-editor to avoid this case.
+
+ * Scripts/commit-log-editor: Added an isCommitLogEditor method and
+ reworked the $editor setting to add this check.
+
+2010-03-25 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add a Toggle Frame Flattening option to QtLauncher.
+ It will be enabled by default on Maemo5 and S60 platforms.
+
+ [Qt] Add enable/disable Frame Flattening option to QtLauncher
+ https://bugs.webkit.org/show_bug.cgi?id=36558
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::applyPrefs):
+ (LauncherWindow::toggleFrameFlattening):
+ (LauncherWindow::createChrome):
+
+2010-03-25 Zoltan Horvath <zoltan@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ svn-create-patch prints a warning for large patches
+ https://bugs.webkit.org/show_bug.cgi?id=32582
+
+ svn-create-patch prints a warning message for larger patches than 20k.
+
+ * Scripts/svn-create-patch:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Renamed early_warning_system.py to earlywarningsystem.py.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/tool/commands/early_warning_system.py: Removed.
+ * Scripts/webkitpy/tool/commands/early_warning_system_unittest.py: Removed.
+ * Scripts/webkitpy/tool/commands/earlywarningsystem.py: Copied from Scripts/webkitpy/tool/commands/early_warning_system.py.
+ * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py: Copied from Scripts/webkitpy/tool/commands/early_warning_system_unittest.py.
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/unittests.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In webkitpy, pushed the unit test import statements in
+ webkitpy/unittests.py into appropriate unittests.py files in the
+ new root-level packages beneath webkitpy.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/webkitpy/common/system/unittests.py:
+ * Scripts/webkitpy/common/unittests.py: Added.
+ * Scripts/webkitpy/python24/unittests.py: Added.
+ * Scripts/webkitpy/tool/unittests.py: Added.
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move WebKitCheckout into the webkitpy.common.checkout
+ package.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/common/checkout/api.py: Copied from Scripts/webkitpy/webkitcheckout.py.
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/webkitcheckout.py: Removed.
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move OutputCapture to webkitpy.common.system.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py:
+ * Scripts/webkitpy/common/net/credentials_unittest.py:
+ * Scripts/webkitpy/common/system/outputcapture.py: Copied from Scripts/webkitpy/outputcapture.py.
+ * Scripts/webkitpy/outputcapture.py: Removed.
+ * Scripts/webkitpy/tool/commands/commandtest.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/multicommandtool_unittest.py:
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move user.py to webkitpy.common.system.
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/net/bugzilla.py:
+ * Scripts/webkitpy/common/net/credentials.py:
+ * Scripts/webkitpy/common/system/user.py: Copied from Scripts/webkitpy/user.py.
+ * Scripts/webkitpy/common/system/user_unittest.py: Copied from Scripts/webkitpy/user_unittest.py.
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/unittests.py:
+ * Scripts/webkitpy/user.py: Removed.
+ * Scripts/webkitpy/user_unittest.py: Removed.
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move executive.py to webkitpy.common.system.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/common/checkout/scm.py:
+ * Scripts/webkitpy/common/checkout/scm_unittest.py:
+ * Scripts/webkitpy/common/config/ports.py:
+ * Scripts/webkitpy/common/config/ports_unittest.py:
+ * Scripts/webkitpy/common/net/credentials.py:
+ * Scripts/webkitpy/common/net/credentials_unittest.py:
+ * Scripts/webkitpy/common/system/executive.py: Copied from Scripts/webkitpy/executive.py.
+ * Scripts/webkitpy/common/system/executive_unittest.py: Copied from Scripts/webkitpy/executive_unittest.py.
+ * Scripts/webkitpy/executive.py: Removed.
+ * Scripts/webkitpy/executive_unittest.py: Removed.
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/bot/queueengine_unittest.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/early_warning_system.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/stepsequence.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/steps/checkstyle.py:
+ * Scripts/webkitpy/tool/steps/preparechangelog.py:
+ * Scripts/webkitpy/unittests.py:
+ * Scripts/webkitpy/webkit_logging_unittest.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move grammary.py into webkitpy.tool.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/grammar.py: Removed.
+ * Scripts/webkitpy/grammar_unittest.py: Removed.
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/grammar.py: Copied from Scripts/webkitpy/grammar.py.
+ * Scripts/webkitpy/tool/grammar_unittest.py: Copied from Scripts/webkitpy/grammar_unittest.py.
+ * Scripts/webkitpy/tool/multicommandtool.py:
+ * Scripts/webkitpy/tool/steps/obsoletepatches.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In webkitpy, moved init/ to common/system/.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/webkitpy/common/system: Copied from WebKitTools/Scripts/webkitpy/init.
+ * Scripts/webkitpy/common/system/logutils.py:
+ * Scripts/webkitpy/common/system/logutils_unittest.py:
+ * Scripts/webkitpy/init: Removed.
+ * Scripts/webkitpy/init/__init__.py: Removed.
+ * Scripts/webkitpy/init/logtesting.py: Removed.
+ * Scripts/webkitpy/init/logutils.py: Removed.
+ * Scripts/webkitpy/init/logutils_unittest.py: Removed.
+ * Scripts/webkitpy/init/unittests.py: Removed.
+ * Scripts/webkitpy/python24/versioning_unittest.py:
+ * Scripts/webkitpy/style_references.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move stepsequence to webkitpy.tool.commands.
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/stepsequence.py: Removed.
+ * Scripts/webkitpy/tool/commands/abstractsequencedcommand.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/stepsequence.py: Copied from Scripts/webkitpy/stepsequence.py.
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Create webkitpy.common.checkout as described in
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/changelogs.py: Removed.
+ * Scripts/webkitpy/changelogs_unittest.py: Removed.
+ * Scripts/webkitpy/commitinfo.py: Removed.
+ * Scripts/webkitpy/commitinfo_unittest.py: Removed.
+ * Scripts/webkitpy/common/checkout: Added.
+ * Scripts/webkitpy/common/checkout/__init__.py: Copied from Scripts/webkitpy/common/__init__.py.
+ * Scripts/webkitpy/common/checkout/changelog.py: Copied from Scripts/webkitpy/changelogs.py.
+ * Scripts/webkitpy/common/checkout/changelog_unittest.py: Copied from Scripts/webkitpy/changelogs_unittest.py.
+ * Scripts/webkitpy/common/checkout/commitinfo.py: Copied from Scripts/webkitpy/commitinfo.py.
+ * Scripts/webkitpy/common/checkout/commitinfo_unittest.py: Copied from Scripts/webkitpy/commitinfo_unittest.py.
+ * Scripts/webkitpy/common/checkout/diff_parser.py: Copied from Scripts/webkitpy/diff_parser.py.
+ * Scripts/webkitpy/common/checkout/diff_parser_unittest.py: Copied from Scripts/webkitpy/diff_parser_unittest.py.
+ * Scripts/webkitpy/common/checkout/scm.py: Copied from Scripts/webkitpy/scm.py.
+ * Scripts/webkitpy/common/checkout/scm_unittest.py: Copied from Scripts/webkitpy/scm_unittest.py.
+ * Scripts/webkitpy/common/net/credentials.py:
+ * Scripts/webkitpy/diff_parser.py: Removed.
+ * Scripts/webkitpy/diff_parser_unittest.py: Removed.
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/scm.py: Removed.
+ * Scripts/webkitpy/scm_unittest.py: Removed.
+ * Scripts/webkitpy/stepsequence.py:
+ * Scripts/webkitpy/style_references.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/comments.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/preparechangelogforrevert.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+ * Scripts/webkitpy/unittests.py:
+ * Scripts/webkitpy/webkitcheckout.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In webkitpy, moved init/versioning.py to python24/.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/test-webkitpy:
+ * Scripts/webkit-patch:
+ * Scripts/webkitpy/init/unittests.py:
+ * Scripts/webkitpy/init/versioning.py: Removed.
+ * Scripts/webkitpy/init/versioning_unittest.py: Removed.
+ * Scripts/webkitpy/python24: Added.
+ * Scripts/webkitpy/python24/__init__.py: Copied from WebKitTools/Scripts/webkitpy/tool/__init__.py.
+ * Scripts/webkitpy/python24/versioning.py: Copied from WebKitTools/Scripts/webkitpy/init/versioning.py.
+ * Scripts/webkitpy/python24/versioning_unittest.py: Copied from WebKitTools/Scripts/webkitpy/init/versioning_unittest.py.
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Create webkitpy.common.net as described in
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/bugzilla.py: Removed.
+ * Scripts/webkitpy/bugzilla_unittest.py: Removed.
+ * Scripts/webkitpy/buildbot.py: Removed.
+ * Scripts/webkitpy/buildbot_unittest.py: Removed.
+ * Scripts/webkitpy/commitinfo.py:
+ * Scripts/webkitpy/common/net: Added.
+ * Scripts/webkitpy/common/net/__init__.py: Added.
+ * Scripts/webkitpy/common/net/bugzilla.py: Copied from Scripts/webkitpy/bugzilla.py.
+ * Scripts/webkitpy/common/net/bugzilla_unittest.py: Copied from Scripts/webkitpy/bugzilla_unittest.py.
+ * Scripts/webkitpy/common/net/buildbot.py: Copied from Scripts/webkitpy/buildbot.py.
+ * Scripts/webkitpy/common/net/buildbot_unittest.py: Copied from Scripts/webkitpy/buildbot_unittest.py.
+ * Scripts/webkitpy/common/net/credentials.py: Copied from Scripts/webkitpy/credentials.py.
+ * Scripts/webkitpy/common/net/credentials_unittest.py: Copied from Scripts/webkitpy/credentials_unittest.py.
+ * Scripts/webkitpy/common/net/irc: Copied from Scripts/webkitpy/irc.
+ * Scripts/webkitpy/common/net/irc/ircbot.py:
+ * Scripts/webkitpy/common/net/irc/ircproxy.py:
+ * Scripts/webkitpy/common/net/irc/messagepump_unittest.py:
+ * Scripts/webkitpy/common/net/irc/threadedmessagequeue_unittest.py:
+ * Scripts/webkitpy/common/net/irc/unittests.py:
+ * Scripts/webkitpy/common/net/networktransaction.py: Copied from Scripts/webkitpy/networktransaction.py.
+ * Scripts/webkitpy/common/net/networktransaction_unittest.py: Copied from Scripts/webkitpy/networktransaction_unittest.py.
+ * Scripts/webkitpy/common/net/statusserver.py: Copied from Scripts/webkitpy/statusserver.py.
+ * Scripts/webkitpy/credentials.py: Removed.
+ * Scripts/webkitpy/credentials_unittest.py: Removed.
+ * Scripts/webkitpy/irc: Removed.
+ * Scripts/webkitpy/irc/__init__.py: Removed.
+ * Scripts/webkitpy/irc/ircbot.py: Removed.
+ * Scripts/webkitpy/irc/ircproxy.py: Removed.
+ * Scripts/webkitpy/irc/messagepump.py: Removed.
+ * Scripts/webkitpy/irc/messagepump_unittest.py: Removed.
+ * Scripts/webkitpy/irc/threadedmessagequeue.py: Removed.
+ * Scripts/webkitpy/irc/threadedmessagequeue_unittest.py: Removed.
+ * Scripts/webkitpy/irc/unittests.py: Removed.
+ * Scripts/webkitpy/networktransaction.py: Removed.
+ * Scripts/webkitpy/networktransaction_unittest.py: Removed.
+ * Scripts/webkitpy/scm_unittest.py:
+ * Scripts/webkitpy/statusserver.py: Removed.
+ * Scripts/webkitpy/tool/bot/queueengine.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/queries_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In webkitpy, renamed MockBugzillaTool to MockTool.
+
+ * Scripts/webkitpy/tool/commands/commandtest.py:
+ * Scripts/webkitpy/tool/commands/queries_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py:
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py:
+
+2010-03-25 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In Scripts/webkitpy, moved webkitport.py and committers.py into
+ common/config/ (also creating common/config/).
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ This is part of the master bug to reorganize webkitpy.
+
+ * Scripts/validate-committer-lists:
+ * Scripts/webkitpy/bugzilla.py:
+ * Scripts/webkitpy/bugzilla_unittest.py:
+ * Scripts/webkitpy/changelogs.py:
+ * Scripts/webkitpy/commitinfo.py:
+ * Scripts/webkitpy/commitinfo_unittest.py:
+ * Scripts/webkitpy/committers.py: Removed.
+ * Scripts/webkitpy/committers_unittest.py: Removed.
+ * Scripts/webkitpy/common: Added.
+ * Scripts/webkitpy/common/__init__.py: Copied from WebKitTools/Scripts/webkitpy/style/__init__.py.
+ * Scripts/webkitpy/common/config: Added.
+ * Scripts/webkitpy/common/config/__init__.py: Copied from WebKitTools/Scripts/webkitpy/style/__init__.py.
+ * Scripts/webkitpy/common/config/committers.py: Copied from WebKitTools/Scripts/webkitpy/committers.py.
+ * Scripts/webkitpy/common/config/committers_unittest.py: Copied from WebKitTools/Scripts/webkitpy/committers_unittest.py.
+ * Scripts/webkitpy/common/config/ports.py: Copied from WebKitTools/Scripts/webkitpy/webkitport.py.
+ * Scripts/webkitpy/common/config/ports_unittest.py: Copied from WebKitTools/Scripts/webkitpy/webkitport_unittest.py.
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ * Scripts/webkitpy/tool/commands/early_warning_system.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/steps/abstractstep.py:
+ * Scripts/webkitpy/unittests.py:
+ * Scripts/webkitpy/webkitport.py: Removed.
+ * Scripts/webkitpy/webkitport_unittest.py: Removed.
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move mock_bugzillatool.py to tool/mocktool.py.
+
+ * Scripts/webkitpy/mock_bugzillatool.py: Removed.
+ * Scripts/webkitpy/tool/commands/commandtest.py:
+ * Scripts/webkitpy/tool/commands/queries_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/mocktool.py: Copied from Scripts/webkitpy/mock_bugzillatool.py.
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py:
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move comments.py and multicommandtool.py to their new
+ home.
+
+ * Scripts/webkitpy/comments.py: Removed.
+ * Scripts/webkitpy/multicommandtool.py: Removed.
+ * Scripts/webkitpy/multicommandtool_unittest.py: Removed.
+ * Scripts/webkitpy/tool/commands/abstractsequencedcommand.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/openbugs.py:
+ * Scripts/webkitpy/tool/commands/queries.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/comments.py: Copied from Scripts/webkitpy/comments.py.
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/tool/multicommandtool.py: Copied from Scripts/webkitpy/multicommandtool.py.
+ * Scripts/webkitpy/tool/multicommandtool_unittest.py: Copied from Scripts/webkitpy/multicommandtool_unittest.py.
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff.py:
+ * Scripts/webkitpy/tool/steps/closepatch.py:
+ * Scripts/webkitpy/tool/steps/reopenbugafterrollout.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Fixup one import statement I didn't find because the .pyc
+ masked the error.
+
+ * Scripts/webkitpy/stepsequence.py:
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move queueengine.py to its new home.
+
+ * Scripts/webkitpy/queueengine.py: Removed.
+ * Scripts/webkitpy/queueengine_unittest.py: Removed.
+ * Scripts/webkitpy/tool/bot/queueengine.py: Copied from Scripts/webkitpy/queueengine.py.
+ * Scripts/webkitpy/tool/bot/queueengine_unittest.py: Copied from Scripts/webkitpy/queueengine_unittest.py.
+ * Scripts/webkitpy/tool/commands/early_warning_system.py:
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Move patchcollection.py to its new home.
+
+ * Scripts/webkitpy/patchcollection.py: Removed.
+ * Scripts/webkitpy/patchcollection_unittest.py: Removed.
+ * Scripts/webkitpy/tool/bot: Added.
+ * Scripts/webkitpy/tool/bot/__init__.py: Added.
+ * Scripts/webkitpy/tool/bot/patchcollection.py: Copied from WebKitTools/Scripts/webkitpy/patchcollection.py.
+ * Scripts/webkitpy/tool/bot/patchcollection_unittest.py: Copied from WebKitTools/Scripts/webkitpy/patchcollection_unittest.py.
+ * Scripts/webkitpy/tool/commands/queues.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-24 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In Scripts/webkitpy, moved steps_references.py and the steps
+ folder into webkitpy/patch.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36093
+
+ * Scripts/webkitpy/steps: Removed.
+ * Scripts/webkitpy/steps/__init__.py: Removed.
+ * Scripts/webkitpy/steps/abstractstep.py: Removed.
+ * Scripts/webkitpy/steps/applypatch.py: Removed.
+ * Scripts/webkitpy/steps/applypatchwithlocalcommit.py: Removed.
+ * Scripts/webkitpy/steps/build.py: Removed.
+ * Scripts/webkitpy/steps/checkstyle.py: Removed.
+ * Scripts/webkitpy/steps/cleanworkingdirectory.py: Removed.
+ * Scripts/webkitpy/steps/cleanworkingdirectorywithlocalcommits.py: Removed.
+ * Scripts/webkitpy/steps/closebug.py: Removed.
+ * Scripts/webkitpy/steps/closebugforlanddiff.py: Removed.
+ * Scripts/webkitpy/steps/closebugforlanddiff_unittest.py: Removed.
+ * Scripts/webkitpy/steps/closepatch.py: Removed.
+ * Scripts/webkitpy/steps/commit.py: Removed.
+ * Scripts/webkitpy/steps/confirmdiff.py: Removed.
+ * Scripts/webkitpy/steps/createbug.py: Removed.
+ * Scripts/webkitpy/steps/editchangelog.py: Removed.
+ * Scripts/webkitpy/steps/ensurebuildersaregreen.py: Removed.
+ * Scripts/webkitpy/steps/ensurelocalcommitifneeded.py: Removed.
+ * Scripts/webkitpy/steps/metastep.py: Removed.
+ * Scripts/webkitpy/steps/obsoletepatches.py: Removed.
+ * Scripts/webkitpy/steps/options.py: Removed.
+ * Scripts/webkitpy/steps/postdiff.py: Removed.
+ * Scripts/webkitpy/steps/postdiffforcommit.py: Removed.
+ * Scripts/webkitpy/steps/postdiffforrevert.py: Removed.
+ * Scripts/webkitpy/steps/preparechangelog.py: Removed.
+ * Scripts/webkitpy/steps/preparechangelogforrevert.py: Removed.
+ * Scripts/webkitpy/steps/promptforbugortitle.py: Removed.
+ * Scripts/webkitpy/steps/reopenbugafterrollout.py: Removed.
+ * Scripts/webkitpy/steps/revertrevision.py: Removed.
+ * Scripts/webkitpy/steps/runtests.py: Removed.
+ * Scripts/webkitpy/steps/steps_unittest.py: Removed.
+ * Scripts/webkitpy/steps/update.py: Removed.
+ * Scripts/webkitpy/steps/updatechangelogswithreview_unittests.py: Removed.
+ * Scripts/webkitpy/steps/updatechangelogswithreviewer.py: Removed.
+ * Scripts/webkitpy/steps/validatereviewer.py: Removed.
+ * Scripts/webkitpy/steps_references.py: Removed.
+ * Scripts/webkitpy/stepsequence.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/steps: Copied from WebKitTools/Scripts/webkitpy/steps.
+ * Scripts/webkitpy/tool/steps/__init__.py:
+ * Scripts/webkitpy/tool/steps/applypatch.py:
+ * Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py:
+ * Scripts/webkitpy/tool/steps/build.py:
+ * Scripts/webkitpy/tool/steps/checkstyle.py:
+ * Scripts/webkitpy/tool/steps/cleanworkingdirectory.py:
+ * Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py:
+ * Scripts/webkitpy/tool/steps/closebug.py:
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff.py:
+ * Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/tool/steps/closepatch.py:
+ * Scripts/webkitpy/tool/steps/commit.py:
+ * Scripts/webkitpy/tool/steps/confirmdiff.py:
+ * Scripts/webkitpy/tool/steps/createbug.py:
+ * Scripts/webkitpy/tool/steps/editchangelog.py:
+ * Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py:
+ * Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py:
+ * Scripts/webkitpy/tool/steps/metastep.py:
+ * Scripts/webkitpy/tool/steps/obsoletepatches.py:
+ * Scripts/webkitpy/tool/steps/postdiff.py:
+ * Scripts/webkitpy/tool/steps/postdiffforcommit.py:
+ * Scripts/webkitpy/tool/steps/postdiffforrevert.py:
+ * Scripts/webkitpy/tool/steps/preparechangelog.py:
+ * Scripts/webkitpy/tool/steps/preparechangelogforrevert.py:
+ * Scripts/webkitpy/tool/steps/promptforbugortitle.py:
+ * Scripts/webkitpy/tool/steps/reopenbugafterrollout.py:
+ * Scripts/webkitpy/tool/steps/revertrevision.py:
+ * Scripts/webkitpy/tool/steps/runtests.py:
+ * Scripts/webkitpy/tool/steps/steps_unittest.py:
+ * Scripts/webkitpy/tool/steps/update.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittests.py:
+ * Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py:
+ * Scripts/webkitpy/tool/steps/validatereviewer.py:
+ * Scripts/webkitpy/tool/steps_references.py: Copied from WebKitTools/Scripts/webkitpy/steps_references.py.
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-24 Eric Seidel <eric@webkit.org>
+
+ Unreviewed, build fix only.
+
+ Abstract LayoutTestResults logic for easier reuse
+ https://bugs.webkit.org/show_bug.cgi?id=36579
+
+ * Scripts/webkitpy/buildbot_unittest.py: Add a missing import.
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Unreviewed. Fix a stray comma to test landing an patch without review.
+
+ * Scripts/webkitpy/commitinfo.py:
+
+2010-03-24 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ In Scripts/webkitpy, moved commands_references.py and the commands
+ folder into webkitpy/patch.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36483
+
+ This is part of master bug 36093 to organize webkitpy.
+
+ * Scripts/webkitpy/commands: Removed.
+ * Scripts/webkitpy/commands/__init__.py: Removed.
+ * Scripts/webkitpy/commands/abstractsequencedcommand.py: Removed.
+ * Scripts/webkitpy/commands/commandtest.py: Removed.
+ * Scripts/webkitpy/commands/download.py: Removed.
+ * Scripts/webkitpy/commands/download_unittest.py: Removed.
+ * Scripts/webkitpy/commands/early_warning_system.py: Removed.
+ * Scripts/webkitpy/commands/early_warning_system_unittest.py: Removed.
+ * Scripts/webkitpy/commands/openbugs.py: Removed.
+ * Scripts/webkitpy/commands/openbugs_unittest.py: Removed.
+ * Scripts/webkitpy/commands/queries.py: Removed.
+ * Scripts/webkitpy/commands/queries_unittest.py: Removed.
+ * Scripts/webkitpy/commands/queues.py: Removed.
+ * Scripts/webkitpy/commands/queues_unittest.py: Removed.
+ * Scripts/webkitpy/commands/queuestest.py: Removed.
+ * Scripts/webkitpy/commands/sheriffbot.py: Removed.
+ * Scripts/webkitpy/commands/sheriffbot_unittest.py: Removed.
+ * Scripts/webkitpy/commands/upload.py: Removed.
+ * Scripts/webkitpy/commands/upload_unittest.py: Removed.
+ * Scripts/webkitpy/commands_references.py: Removed.
+ * Scripts/webkitpy/tool/commands: Copied from WebKitTools/Scripts/webkitpy/commands.
+ * Scripts/webkitpy/tool/commands/commandtest.py:
+ * Scripts/webkitpy/tool/commands/download.py:
+ * Scripts/webkitpy/tool/commands/download_unittest.py:
+ * Scripts/webkitpy/tool/commands/early_warning_system.py:
+ * Scripts/webkitpy/tool/commands/early_warning_system_unittest.py:
+ * Scripts/webkitpy/tool/commands/openbugs_unittest.py:
+ * Scripts/webkitpy/tool/commands/queries_unittest.py:
+ * Scripts/webkitpy/tool/commands/queues_unittest.py:
+ * Scripts/webkitpy/tool/commands/queuestest.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot.py:
+ * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py:
+ * Scripts/webkitpy/tool/commands/upload.py:
+ * Scripts/webkitpy/tool/commands/upload_unittest.py:
+ * Scripts/webkitpy/tool/commands_references.py: Copied from WebKitTools/Scripts/webkitpy/commands_references.py.
+ * Scripts/webkitpy/tool/main.py:
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Rename webkitpy.patch to webkitpy.tool
+ https://bugs.webkit.org/show_bug.cgi?id=36580
+
+ This is in preparation for the great webkitpy naming cleanup.
+
+ * Scripts/webkit-patch:
+ * Scripts/webkitpy/patch: Removed.
+ * Scripts/webkitpy/patch/__init__.py: Removed.
+ * Scripts/webkitpy/patch/patcher.py: Removed.
+ * Scripts/webkitpy/tool: Copied from WebKitTools/Scripts/webkitpy/patch.
+ * Scripts/webkitpy/tool/main.py: Copied from WebKitTools/Scripts/webkitpy/patch/patcher.py.
+ * Scripts/webkitpy/tool/patcher.py: Removed.
+
+2010-03-24 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Abstract LayoutTestResults logic for easier reuse
+ https://bugs.webkit.org/show_bug.cgi?id=36579
+
+ * Scripts/webkitpy/buildbot.py:
+ - Split out logic into new LayoutTestResults class.
+ * Scripts/webkitpy/buildbot_unittest.py:
+ - Rename the testing class to match.
+ * Scripts/webkitpy/commands/queries.py:
+ - Use the new LayoutTestResults class.
+
+2010-03-24 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move Bugzilla.prompt_for_component to User.prompt_with_list for re-use
+ https://bugs.webkit.org/show_bug.cgi?id=36577
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Move prompt_for_component to User
+ * Scripts/webkitpy/commands/queries.py:
+ - Add a missing argument_names declaration.
+ * Scripts/webkitpy/user.py:
+ - Add prompt_with_list
+ - Make staticmethods classmethods for easier mocking
+
+2010-03-24 David Kilzer <ddkilzer@apple.com>
+
+ <http://webkit.org/b/36572> commit-log-editor: thinks mergeChangeLogs.pl is a ChangeLog file
+
+ Reviewed by Eric Seidel.
+
+ Fixes the following error when committing a file with
+ "ChangeLog" in the name that isn't a ChangeLog (like
+ mergeChangeLogs.pl from r56471 and r56472):
+
+ $ git commit .
+ Can't open WebKitTools/Scripts/webkitperl/VCSUtils_unittest/mergeChangeLog at commit-log-editor line 132.
+ error: There was a problem with the editor 'commit-log-editor'.
+ Please supply the message using either -m or -F option.
+
+ * Scripts/commit-log-editor: Added '$' to anchor "ChangeLog" to
+ the end of the file name when searching for ChangeLog files in a
+ commit.
+
+2010-03-24 David Kilzer <ddkilzer@apple.com>
+
+ <http://webkit.org/b/36570> resolve-ChangeLogs: fall back to git-merge-file if ChangeLog can't be merged
+
+ Reviewed by Eric Seidel.
+
+ * Scripts/resolve-ChangeLogs: Switched to exec git-merge-file if
+ the merge attempt fails.
+
+2010-03-24 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ webkit-patch or pre-commit hook should validate reviewer lines before committing
+ https://bugs.webkit.org/show_bug.cgi?id=26927
+
+ Validate that patches have valid reivewers listed in their ChangeLogs
+ before landing. For patches without reviewers can be landed if their
+ ChangeLogs state that they are unreviewed.
+
+ * Scripts/webkitpy/changelogs.py:
+ * Scripts/webkitpy/commands/download.py:
+ * Scripts/webkitpy/commitinfo.py:
+ * Scripts/webkitpy/commitinfo_unittest.py:
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ * Scripts/webkitpy/steps/__init__.py:
+ * Scripts/webkitpy/steps/validatereviewer.py: Added.
+
+2010-03-19 Holger Hans Peter Freyther <zecke@selfish.org>
+
+ Reviewed by David Levin.
+
+ Undefined names reported by pyflakes in python scripts
+ https://bugs.webkit.org/show_bug.cgi?id=36403
+
+ Attempt to use names that exist or can exist in the lexical
+ scope instead of not being available at all.
+
+ * Scripts/webkitpy/changelogs.py: Use self._content.
+ * Scripts/webkitpy/layout_tests/port/base.py: Use os.stat
+ * Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py: Use self._port
+ * Scripts/webkitpy/style/processors/cpp_unittest.py: Use expected_message_re
+
+2010-03-24 David Kilzer <ddkilzer@apple.com>
+
+ <http://webkit.org/b/36560> resolve-ChangeLogs: git-rebase fails when resolve-ChangeLogs can't merge
+
+ Reviewed by Eric Seidel.
+
+ When resolve-ChangeLogs fails to merge a patch while running as
+ a git merge driver, it deletes the original file, which causes
+ an internal failure and stops git mid-merge:
+
+ fatal: Failed to execute internal merge
+
+ The fix is to use the --force switch with patch so that it will
+ always attempt to apply the patch. (The change in
+ mergeChangeLogs() for the previous commit also fixed this, but
+ adding --force also prevents any potential user interaction that
+ patch may want to display.)
+
+ * Scripts/VCSUtils.pm:
+ (mergeChangeLogs): Added --force switch to patch command. Also
+ changed to use the exit status from the patch command to
+ determine the return value for this method.
+ * Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl: Added
+ test to cover this bug.
+
+2010-03-24 David Kilzer <ddkilzer@apple.com>
+
+ <http://webkit.org/b/36560> resolve-ChangeLogs: move mergeChanges() into VCSUtils package
+
+ Reviewed by Eric Seidel.
+
+ * Scripts/VCSUtils.pm:
+ (mergeChangeLogs): Copied from mergeChanges() in
+ resolve-ChangeLogs and renamed. Added method documentation.
+ Fixed bug found by new tests where the original file to be
+ patched was deleted when cleaning up after a traditinal rejected
+ patch failed to apply.
+ * Scripts/resolve-ChangeLogs: Switched to using
+ mergeChangeLogs().
+ (mergeChanges): Moved to VCSUtils.pm and renamed to
+ mergeChangeLogs().
+ * Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl: Added.
+
+2010-03-24 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add support for qt's unassigned list to webkit-patch assign-to-committer
+ https://bugs.webkit.org/show_bug.cgi?id=36559
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Move Bugzilla.unassigned_email into Bug and make it a set.
+ * Scripts/webkitpy/bugzilla_unittest.py:
+ - Test the new Bug.is_unassigned method
+ * Scripts/webkitpy/commands/upload.py:
+ - Use the new Bug.is_unassigned method instead of an explicit ==
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ - Bugzilla.unassigned_email no longer needs mocking
+
+2010-03-24 Kent Hansen <kent.hansen@nokia.com>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Rename QWebSettings::XSSAuditorEnabled to XSSAuditingEnabled
+ https://bugs.webkit.org/show_bug.cgi?id=36522
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setXSSAuditorEnabled): Use the new name.
+
+2010-03-24 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Added to check-webkit-style support for a --debug flag.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36100
+
+ The --debug flag adjusts the logging level to DEBUG and
+ includes the logger name and level in each log message.
+
+ * Scripts/check-webkit-style:
+ - Changed the code to check for the --debug flag and pass
+ the result to the configure_logging() method.
+ * Scripts/webkitpy/style/checker.py:
+ - Added an is_debug parameter to configure_logging().
+ - Refactored configure_logging() by adding calls to
+ the following two methods: _create_log_handlers() and
+ _create_debug_log_handlers().
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Added unit tests for configure_logging() with is_debug True
+ by splitting the ConfigureLoggingTest class into
+ ConfigureLoggingTest and ConfigureLoggingTestBase, and
+ adding ConfigureLoggingDebugTest.
+ * Scripts/webkitpy/style/optparser.py:
+ - Updated the usage string.
+ - Added an is_debug data attribute to the CommandOptionValues
+ class.
+ - Added support for the --debug flag to the ArgumentParser.parse()
+ method.
+ - Also added extra error information to the parse() method in
+ the case of an invalid flag.
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Updated the unit tests as necessary.
+ - Also fixed an issue with the CommandOptionValuesTest.test_eq()
+ unit test.
+
+2010-03-23 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add TestNavigationController and TestWebWorker
+ https://bugs.webkit.org/show_bug.cgi?id=36489
+
+ Add TestNavigationController and TestWebWorker classes, which are
+ going to be used by DumpRenderTree Chromium port. These files are
+ based on:
+ - src/webkit/tools/test_shell/test_navigation_controller.{cc,h}
+ - src/webkit/tools/test_shell/test_web_worker.h
+ of Chromium rev.40492.
+
+ TestNavigationController has non-style changes.
+ - Change ContentState type: binary string -> WebHistoryItem
+ - Remove TestShell dependency by introducing NavigationHost interface.
+
+ * DumpRenderTree/chromium/TestNavigationController.cpp: Added.
+ * DumpRenderTree/chromium/TestNavigationController.h: Added.
+ * DumpRenderTree/chromium/TestWebWorker.h: Added.
+
+2010-03-23 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add support for revision -> build lookup in buildbot.py and layout test result parsing
+ https://bugs.webkit.org/show_bug.cgi?id=36474
+
+ * Scripts/webkitpy/bugzilla_unittest.py: Added a FIXME about sharing code.
+ * Scripts/webkitpy/buildbot.py:
+ - Add support for looking up builds by revision number.
+ - Add support for fetching and parsing results.html files from buildbot.
+ - build_for_revision has an allow_failed_lookups option to work around the fact that
+ our buildbot's xmlrpc calls return failure on old revision numbers.
+ - Add parsing support for twisted directory listings.
+ * Scripts/webkitpy/buildbot_unittest.py:
+ - Unit test all the new code.
+ * Scripts/webkitpy/commands/queries.py:
+ - Add a new results-for command which prints all the results for a given revision (very slow due to slow revision lookup)
+
+2010-03-23 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Adam Roben.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36048
+
+ Detect if the Windows Platform SDK is missing when building with
+ Visual C++ Express Edition and inform the user to download it.
+
+ * Scripts/webkitdirs.pm:
+
+2010-03-23 Darin Adler <darin@apple.com>
+
+ Tell Subversion about more directories that expect to have .pyc files.
+
+ * Scripts/webkitpy: Modified property svn:ignore.
+ * Scripts/webkitpy/irc: Added property svn:ignore.
+
+2010-03-23 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Adam Barth.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36149
+
+ Import the GNU readline interface to modify the behavior
+ of raw_input so as to provide line editing support. In
+ particular this will prevent "delete" characters from
+ appearing in the returned value for function raw_input.
+
+ * Scripts/webkitpy/user.py:
+
+2010-03-23 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Remove support for Qt v4.4
+ https://bugs.webkit.org/show_bug.cgi?id=36389
+
+ * DumpRenderTree/qt/main.cpp:
+ (main):
+ * QtLauncher/mainwindow.cpp:
+ (MainWindow::MainWindow):
+
+2010-03-22 Csaba Osztrogonác <ossy@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Make build_webkit_command() pass MAKEFLAGS environment variable to make.
+ https://bugs.webkit.org/show_bug.cgi?id=36440
+
+ * Scripts/webkitpy/webkitport.py:
+ * Scripts/webkitpy/webkitport_unittest.py:
+
+2010-03-22 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Change baseline_path() to point to the upstream locations for the
+ Chromium ports. Also change the reabselining scripts to use the
+ correct functions to get the baseline directories, and fix the
+ script's sys.path to pull in simplejson correctly.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36417
+
+ * Scripts/rebaseline-chromium-webkit-tests:
+ - fix sys.path to pick up simplejson properly
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ - change baseline_path() to use webkit_baseline_path()
+ - error out correctly if we can't find the chromium base dir
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ - call baseline_path(), not chromium_baseline_path()
+
+2010-03-22 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Refactored the cpu_count() code in executive.py.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36437
+
+ * Scripts/webkitpy/executive.py:
+ - Moved the import of the multiprocessing module to the top
+ of the file rather than importing from within a function.
+
+2010-03-22 Antonio Gomes <tonikitoo@webkit.org>
+
+ Unreviewed.
+
+ Rolling out r56183: http://trac.webkit.org/changeset/56183
+
+ https://bugs.webkit.org/show_bug.cgi?id=36244
+
+ Need to roll out because this patch will be re-worked by the author
+ and other reviewers agreed on it.
+
+2010-03-22 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Created a function for getting a module-specific logging.logger
+ based on the __file__ value of the module.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35821
+
+ This function allows us to get the module-specific logger for
+ a module without having to hard-code the fully-qualified name
+ of the module in the module itself. The code can be the same
+ in every case: "_log = logutils.get_logger(__file__)".
+
+ * Scripts/webkitpy/init/logutils.py: Added.
+ - Added a module with a get_logger() function to return
+ a module-specific logger based on the module's __file__
+ variable.
+
+ * Scripts/webkitpy/init/logutils_unittest.py: Added.
+ - Added unit tests for logutils.py.
+
+ * Scripts/webkitpy/init/unittests.py:
+ - Added logutils_unittest to the list of imports.
+
+2010-03-22 Kenneth Rohde Christiansen <kenneth@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] REGRESSION (r56209): fast/media/print-restores-previous-mediatype.htm crashes
+ https://bugs.webkit.org/show_bug.cgi?id=36386
+
+ Fix the regression by implementing a null printer for Qt DRT.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::NullPrinter::NullPaintEngine::begin):
+ (WebCore::NullPrinter::NullPaintEngine::end):
+ (WebCore::NullPrinter::NullPaintEngine::type):
+ (WebCore::NullPrinter::NullPaintEngine::drawPixmap):
+ (WebCore::NullPrinter::NullPaintEngine::updateState):
+ (WebCore::NullPrinter::paintEngine):
+ (WebCore::DumpRenderTree::dryRunPrint):
+
+2010-03-20 Martin Robinson <mrobinson@webkit.org>
+
+ Reviewed by Xan Lopez.
+
+ [GTK] eventSender.zoomPageOut() bug?
+ https://bugs.webkit.org/show_bug.cgi?id=30575
+
+ Make zoomPage{In/Out}Callback respect the 1.2f zoom factor that DRT should be using.
+
+ * DumpRenderTree/gtk/EventSender.cpp:
+ (zoomIn): Added.
+ (zoomOut): Added.
+ (textZoomInCallback): Use zoomIn helper function.
+ (textZoomOutCallback): Use zoomOut helper function.
+ (zoomPageInCallback): Use zoomIn helper function, which respects zoom factor.
+ (zoomPageOutCallback): Use zoomOut helper function, which respects zoom factor.
+
+2010-03-20 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fix after recent database API change.
+
+ * wx/browser/browser.cpp:
+ (MyApp::OnInit):
+
+2010-03-20 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Renamed UnitTestLogStream to TestLogStream in webkitpy.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36099
+
+ TestLogStream is more concise and more consistent with the name of
+ the module (logtesting rather than logunittesting) and its main
+ class (LogTesting rather than LogUnitTesting).
+
+ * Scripts/webkitpy/init/logtesting.py:
+ - Renamings.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Renamings.
+
+ * Scripts/webkitpy/style_references.py:
+ - Renamings.
+
+2010-03-20 Kevin Ollivier <kevino@theolliviers.com>
+
+ [wx] Build fixes for new method in LayoutTestController.
+
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::setSpatialNavigationEnabled):
+ * wx/build/settings.py:
+
+2010-03-20 Dimitri Glazkov <dglazkov@chromium.org>
+
+ Unreviewed, tool fix.
+
+ Remove vestiges of downstream directory names to unbreak rebaselining tool.
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py: Changed paths to use WebKit repo.
+
+2010-03-20 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Re-enable the downstream test_expectations overrides file that was
+ disabled in bug 36396 / r56287.
+
+ https://bugs.chromium.org/show_bug.cgi?id=36401
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-03-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Disable the downstream override expectations temporarily to allow
+ us to test that we've upstreamed everything correctly. Also, stop
+ looking at the downstream baselines at all (now you will only be
+ able to update baselines upstream). In theory this should work, but
+ if we need to we can always add the downstream dirs back in.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36396
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+
+2010-03-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ fix typo in chromium test expectations overrides routine
+ https://bugs.webkit.org/show_bug.cgi?id=36397
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-03-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Flip the Chromium ports to look first for the test expectations
+ in LayoutTests/platform/chromium and only afterwards look in the
+ Chromium repo downstream for overrides.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36326
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-03-19 James Hawkins <jhawkins@chromium.org>
+
+ Unreviewed.
+
+ Add myself to the committers list.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Fix SheriffBot exception lock when we can't retrieve the first build
+ from buildbot. (I'll ask Eric to review this change after the fact,
+ but he's at lunch and I want to get the bot unlocked.)
+
+ * Scripts/webkitpy/buildbot.py:
+ * Scripts/webkitpy/buildbot_unittest.py:
+
+2010-03-19 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Darin Adler.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36380
+ websocket/tests/frame-lengths.html times out on Tiger bot
+
+ https://bugs.webkit.org/show_bug.cgi?id=35041
+ websocket/tests/frame-lengths.html / websocket/tests/simple-stress.html fail on Windows bot
+
+ Double the timeout (from 15 seconds to 30 seconds). We can increase it more, if necessary -
+ sampling the DRT process on Mac OS X takes much longer anyway, so it's better to avoid
+ timing out than to detect it early.
+
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setWaitToDump):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::waitUntilDone):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ * Scripts/run-webkit-tests:
+
+2010-03-19 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Unreviewed.
+
+ Buildfix for Qt v4.5.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::LauncherWindow):
+ (LauncherWindow::applyZoom):
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Operational tweaks to SheriffBot
+ https://bugs.webkit.org/show_bug.cgi?id=36385
+
+ These changes aren't pretty, but they're helpful to make SheriffBot
+ work operationally. I plan to iterate in these areas, but I wanted to
+ get this patch landed so I could be running the bot against TOT.
+
+ * Scripts/webkitpy/commands/sheriffbot.py:
+ * Scripts/webkitpy/irc/ircbot.py:
+ * Scripts/webkitpy/thirdparty/autoinstalled/__init__.py:
+
+2010-03-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Help sheriff-bot avoid warning about flaky tests (and add more unit testing)
+ https://bugs.webkit.org/show_bug.cgi?id=36354
+
+ * Scripts/webkitpy/buildbot.py:
+ - Make Build creation easier to Mock and test
+ * Scripts/webkitpy/buildbot_unittest.py:
+ - Test finding green to red transitions and suspect revisions
+ * Scripts/webkitpy/commands/queries.py:
+ - Make what-broke note when builders have only failed once.
+
+2010-03-19 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Fix the rebaselining tool, which was broken by r36324 when I
+ added the concept of overridding expectations.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36374
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Misc bug fixes to make the SheriffBot actually work
+ https://bugs.webkit.org/show_bug.cgi?id=36355
+
+ With these changes, I can actually run the sheriff-bot from start to
+ finish.
+
+ * Scripts/webkitpy/irc/ircproxy.py:
+ * Scripts/webkitpy/patch/patcher.py:
+ * Scripts/webkitpy/statusserver.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Actually import the sheriff-bot command so we can run it. Also, move
+ the bot to #webkit-test so it doesn't cause a ruckus while we test it.
+
+ * Scripts/webkitpy/patch/patcher.py:
+ * Scripts/webkitpy/irc/ircbot.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Second cut at SheriffBot
+ https://bugs.webkit.org/show_bug.cgi?id=36353
+
+ This patch should contain a complete SheriffBot that's capable of
+ saying reasonable things on IRC. I had to refactor the use of
+ CommitInfo to make the SheriffBot testable, but I did the minimum
+ necessary. We should grow webkitcheckout over time to contain the
+ knowledge of ChangeLogs from scm.
+
+ * Scripts/webkitpy/commands/sheriffbot.py:
+ * Scripts/webkitpy/commands/sheriffbot_unittest.py:
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ * Scripts/webkitpy/patch/patcher.py:
+ * Scripts/webkitpy/webkitcheckout.py: Added.
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Fix Hyatt's IRC nickname.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Add IRC nicknames for the non-reviewer committers.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ More reviewer IRC nicknames.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Add a bunch of IRC nicknames for reviewers.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-19 Zoltan Horvath <zoltan@webkit.org>
+
+ Reviewed by Oliver Hunt.
+
+ Added USE_SYSTEM_MALLOC flag to build-webkit
+ https://bugs.webkit.org/show_bug.cgi?id=21272
+
+ Add system-alloc flag to build-webkit. It makes easy to switch
+ between system allocator and TCmalloc.
+
+ * Scripts/build-webkit:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Unreviewed "build" fix.
+
+ Of course, I caused a regression in the file that isn't tested. :(
+
+ * Scripts/webkitpy/statusserver.py:
+
+2010-03-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add basic "who-broke-it" command and revision -> broken builder association code
+ https://bugs.webkit.org/show_bug.cgi?id=36352
+
+ The "what-broke" command prints builders and what revisions we suspect
+ broke them. who-broke-it prints revisions and what builders we suspect
+ they broke. The sheriff-bot needs this revision to broken builder mapping
+ so this change adds it!
+
+ * Scripts/webkitpy/buildbot.py:
+ * Scripts/webkitpy/commands/queries.py:
+
+2010-03-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Simplify BuildBot core builder code for easier re-use
+ https://bugs.webkit.org/show_bug.cgi?id=36350
+
+ I simply couldn't see anything through all this Yak-hair.
+
+ * Scripts/webkitpy/buildbot.py:
+ * Scripts/webkitpy/commands/queries.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ committers.py should know IRC nicknames
+ https://bugs.webkit.org/show_bug.cgi?id=36349
+
+ I'll add the actual nicknames in another patch.
+
+ * Scripts/webkitpy/committers.py:
+ * Scripts/webkitpy/committers_unittest.py:
+
+2010-03-18 Anders Bakken <agbakken@gmail.com>
+
+ Reviewed by Simon Hausmann.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36318
+
+ QtLauncher runs as a GuiClient by default in Qt Embedded which will
+ make it try to connect to an existing GuiServer. This patch makes it
+ run like a stand-alone app.
+
+ * QtLauncher/main.cpp:
+ (LauncherApplication::LauncherApplication):
+
+2010-03-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Move find_green_to_red_transition out of "what-broke" onto Builder for easier re-use
+ https://bugs.webkit.org/show_bug.cgi?id=36345
+
+ * Scripts/webkitpy/buildbot.py:
+ * Scripts/webkitpy/commands/queries.py:
+
+2010-03-19 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Actually pass the IRC password to the IRC object
+ https://bugs.webkit.org/show_bug.cgi?id=36346
+
+ I wanted to do this before, but both patches were in flight. This
+ patch finally closes the loop and makes the IRCProxy system complete.
+
+ * Scripts/webkitpy/patch/patcher.py:
+
+2010-03-18 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add a StatusServer front end to the SVNRevision table on QueueStatusServer
+ https://bugs.webkit.org/show_bug.cgi?id=36344
+
+ No test because Browser was too hard to mock. :( I couldn't figure
+ out how to make Mock be a dictionary as well as an object.
+
+ * Scripts/webkitpy/statusserver.py:
+
+2010-03-18 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Split out CommitInfo class and add unit tests
+ https://bugs.webkit.org/show_bug.cgi?id=36343
+
+ Move more logic out of "what-broke" into a shared CommitInfo
+ class so that it can be used by other commands and unit tested.
+
+ * Scripts/webkitpy/commands/queries.py:
+ * Scripts/webkitpy/commitinfo.py: Added.
+ * Scripts/webkitpy/commitinfo_unittest.py: Added.
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-18 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Fix LayoutTests/http/tests/appcache/max-size.html
+ https://bugs.webkit.org/show_bug.cgi?id=36207
+
+ Implement setAppCacheMaximumSize() for Qt.
+
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setAppCacheMaximumSize):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-03-18 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ QueueStatusServer should be able to log broken bots
+ https://bugs.webkit.org/show_bug.cgi?id=36341
+
+ We need to add a new table to the QueueStatusServer to store persistent
+ information for the SheriffBot. The new table will keep track of which
+ bots each SVN revision broke.
+
+ * QueueStatusServer/handlers/__init__.py:
+ * QueueStatusServer/handlers/svnrevision.py: Added.
+ * QueueStatusServer/handlers/updatebase.py: Added.
+ * QueueStatusServer/handlers/updatestatus.py:
+ * QueueStatusServer/handlers/updatesvnrevision.py: Added.
+ * QueueStatusServer/index.yaml:
+ * QueueStatusServer/main.py:
+ * QueueStatusServer/model/__init__.py:
+ * QueueStatusServer/model/svnrevision.py: Added.
+ * QueueStatusServer/templates/updatesvnrevision.html: Added.
+
+2010-03-18 Kenneth Rohde Christiansen <kenneth@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ Add a new method to the Qt LayoutTestController for
+ changing media type and make the DRT support dry-run printing.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::DumpRenderTree):
+ (WebCore::DumpRenderTree::dryRunPrint):
+ * DumpRenderTree/qt/DumpRenderTreeQt.h:
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setMediaType):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-03-18 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Add upstream LayoutTests/platform/chromium* directories to the
+ baseline search path for new-run-webkit-tests in preparation for
+ upstreaming all of the Chromium baselines. Note that this does
+ not actually create the directories themselves, but that's okay.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36324
+
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+
+2010-03-18 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Anders Carlsson.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36327
+ Test that a plug-in can override Node methods of its element
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (normalizeOverride):
+ (pluginInvoke):
+ Override "normalize", and call back to let a test know that the plug-in was called.
+
+2010-03-17 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Add the concept of an "overrides" file for expectations so that we
+ can store test_expectations both upstream and downstream for a port
+ that runs both in webkit.org and in a separate repository (like
+ Chromium). Also add some unit tests for the expectations module.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36249
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations_test.py: Added.
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-03-18 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add an overlay QGraphicsTextItem to QtLauncher so we can display FPS info
+ on the launcher and not on the terminal anymore.
+
+ [Qt] QtLauncher's FPS info should be displayed on an overlay text item
+ https://bugs.webkit.org/show_bug.cgi?id=36244
+
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::WebViewGraphicsBased):
+ (WebViewGraphicsBased::setFrameRateMeasurementEnabled):
+ (WebViewGraphicsBased::updateFrameRate):
+ * QtLauncher/webview.h:
+
+2010-03-18 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ First cut at SheriffBot
+ https://bugs.webkit.org/show_bug.cgi?id=36253
+
+ This patch contains a first attempt at writing a sheriff bot.
+ Currently, we're missing the logic that actually finds the SVN revision
+ numbers to complain about, but once we have that, we'll have the rest
+ of the infrustructure to ping IRC and to file bugs.
+
+ There's a lot to fill in for the SheriffBot, but this patch give us the
+ framework in which to do it.
+
+ This patch required a bit of refactoring of AbstractQueue because
+ SheriffBot is the first bot that doesn't process patches (it processes
+ SVN revisions). Accordingly, I've factored out AbstractPatchQueue to
+ hold the parts of AbstractQueue that are specific to dealing with
+ patches. Some of the choices here might not be obvious yet, but we can
+ tweak them as our needs become clearer.
+
+ * Scripts/webkitpy/commands/queues.py:
+ * Scripts/webkitpy/commands/queues_unittest.py:
+ * Scripts/webkitpy/commands/sheriffbot.py: Added.
+ * Scripts/webkitpy/commands/sheriffbot_unittest.py: Added.
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ Added a MockIRC object to the mock tool.
+ * Scripts/webkitpy/multicommandtool.py:
+ Added a finalize method so the tool can disconnect from IRC
+ cleanly instead of just droping the socket.
+ * Scripts/webkitpy/multicommandtool_unittest.py:
+ * Scripts/webkitpy/patch/patcher.py:
+ Added support for talking to IRC.
+ * Scripts/webkitpy/unittests.py:
+ We should add a commands/unittests.py file at some point to make
+ the commands module more self-contained.
+
+2010-03-18 Antti Koivisto <koivisto@iki.fi>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36102
+ [Qt] Scaling control API for tiled backing store
+
+ Add animated smooth zooming to Qt launcher when in tiled mode.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::LauncherWindow):
+ (LauncherWindow::zoomAnimationFinished):
+ (LauncherWindow::applyZoom):
+ (LauncherWindow::zoomIn):
+ (LauncherWindow::zoomOut):
+ * QtLauncher/webview.h:
+ (WebViewGraphicsBased::graphicsWebView):
+
+2010-03-18 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Support using IRC accounts with a password
+ https://bugs.webkit.org/show_bug.cgi?id=36287
+
+ Add a global option to specify an IRC password so we can use the
+ sheriffbot account (which needs a password).
+
+ * Scripts/webkitpy/irc/ircbot.py:
+ * Scripts/webkitpy/irc/ircproxy.py:
+ * Scripts/webkitpy/patch/patcher.py:
+
+2010-03-18 Eric Seidel <eric@webkit.org>
+
+ Just fixing missing parenthesis typo, no review.
+
+ * Scripts/webkitpy/commands/queries.py: '%' has higher precedence than 'or', use parentheses.
+
+2010-03-18 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Adam Roben and Anders Carlsson.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36210
+ plugins/resize-from-plugin.html fails on some platforms
+
+ Turns out that most platforms don't use "cross-platform" main.cpp. Copied code added for
+ the test to their versions of the file.
+
+ * DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp:
+ (webkit_test_plugin_set_window):
+ * DumpRenderTree/win/TestNetscapePlugin/main.cpp:
+ (NPP_SetWindow):
+
+2010-03-18 Leandro Pereira <leandro@profusion.mobi>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add support to run-launcher to open the EFL example browser.
+ http://webkit.org/b/36181
+
+ * Scripts/webkitdirs.pm:
+ * Scripts/run-launcher:
+
+2010-03-18 Sergio Villar Senin <svillar@igalia.com>
+
+ Reviewed by Xan Lopez.
+
+ [GTK] Failing tests http/tests/misc/image-blocked-src-change.html
+ & http/tests/misc/image-blocked-src-no-change.html
+ https://bugs.webkit.org/show_bug.cgi?id=36227
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewConsoleMessage): print only the file name instead of the
+ whole URI when printing messages with local URI's
+
+2010-03-18 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Teach what-broke how to look up reviewer and author Committer objects by name
+ https://bugs.webkit.org/show_bug.cgi?id=36264
+
+ * Scripts/webkitpy/commands/queries.py:
+ - Add committer_by_name lookups for both reviewer and author
+ - Improve printing in the cases where lookups fail.
+ * Scripts/webkitpy/committers.py:
+ - Add committer_by_name
+ * Scripts/webkitpy/committers_unittest.py:
+ - Test committer_by_name
+
+2010-03-17 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ create-rollout should actually fill out the description
+ https://bugs.webkit.org/show_bug.cgi?id=36261
+
+ * Scripts/webkitpy/commands/download.py:
+ The % operator was applied to the wrong string.
+ * Scripts/webkitpy/commands/download_unittest.py:
+ * Scripts/webkitpy/commands/upload_unittest.py:
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ Add support for seeing what we actually do with create_bug.
+
+2010-03-17 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Accelerated Compositing is now default on QtWebKit so the option
+ in QtLauncher must be true as default.
+
+ [Qt] QtLauncher's Accelerated Compositing option must be true as default
+ https://bugs.webkit.org/show_bug.cgi?id=36234
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::applyPrefs):
+ (LauncherWindow::toggleAcceleratedCompositing):
+ (LauncherApplication::handleUserOptions):
+
+2010-03-17 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Commit queue should ignore (probably red) builders when landing rollouts
+ https://bugs.webkit.org/show_bug.cgi?id=36169
+
+ When landing a rollout, the builders are probably red, so we need to
+ ignore them in the subprocess too. Also, we might as well update the
+ working copy because we haven't validated anything about the current
+ revision prior to trying to land.
+
+ This change is testable, but it requires changing the mock executive to
+ log its arguments. That will generate a lot of expectation changes, so
+ I'd like to do that in a separate patch.
+
+ * Scripts/webkitpy/commands/queues.py:
+
+2010-03-17 Chang Shu <chang.shu@nokia.com>
+
+ Reviewed by Laszlo Gombos.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36139
+ [Qt] Clean up cache each time DumpRenderTree starts. This behavior
+ matches other platforms, such as mac and gtk.
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::DumpRenderTree):
+
+2010-03-17 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Turns out this needs to be a string.
+
+ * Scripts/webkitpy/bugzilla.py:
+
+2010-03-17 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add more infrastructure for sheriff-bot, including making what-broke more awesome
+ https://bugs.webkit.org/show_bug.cgi?id=36254
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Made the various URL methods return None when passed None.
+ * Scripts/webkitpy/bugzilla_unittest.py:
+ - Test that the url methods work as expected.
+ * Scripts/webkitpy/buildbot.py:
+ - Add a static Build.build_url so that its possible to generate a build url without a Build object.
+ - Give users a URL in _fetch_xmlrpc_build_dictionary error message.
+ * Scripts/webkitpy/changelogs.py:
+ - Add a new ChangeLogEntry class to encapsulate entry-parsing logic.
+ - Add is_path_to_changelog to greatly simplify SCM.modified_changelogs code.
+ - Make ChangeLog.parse_latest_entry_from_file a public method.
+ * Scripts/webkitpy/changelogs_unittest.py:
+ - Add tests for new ChangeLog entry parsing.
+ * Scripts/webkitpy/commands/queries.py:
+ - Make "what-broke" not print "ok" builders, only failing ones.
+ - Print much more information on failing builders, including links and authorship/reviewer information.
+ * Scripts/webkitpy/commands/queues_unittest.py:
+ - Use a fake_checkout path since fixing the cwd (as part of fixing scm_unittests.py) was breaking tests.
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ - Move MockSCM away from using os.getcwd() as that was fragile (and wrong).
+ * Scripts/webkitpy/patch/patcher.py:
+ - Remove code which was broken now that this file has moved.
+ - Code was also redundant now that SCM.find_checkout_root() exists.
+ * Scripts/webkitpy/scm.py:
+ - Greatly simplify modified_changelogs now that I understand list comprehensions.
+ - Expect ChangeLogEntry objects instead of raw strings.
+ - Add changed_files_for_revision, committer_email_for_revision and contents_at_revision
+ - Add commit_with_message argument to all sites since someone half-added it before. :(
+ - Get rid of copy/paste code using _status_regexp()
+ * Scripts/webkitpy/scm_unittest.py:
+ - Fix these tests!
+ - Add new tests for new scm code.
+ - Fix spelling of "awsome" to "awesome".
+
+2010-03-17 Daniel Bates <dbates@rim.com>
+
+ Rubber-stamped by David Levin.
+
+ Add myself to the list of reviewers.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-17 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Change post-rollout to create-rollout and have it make a new bug
+ instead of posting the rollout to the old bug.
+ https://bugs.webkit.org/show_bug.cgi?id=36250
+
+ The new bug blocks the old bug instead of adding more complexity to the
+ old bug. One tricky question is whether to create the bug if we're
+ unable to create a rollout patch. In this patch, we do create the bug,
+ but we might revist this question in the future.
+
+ * Scripts/webkitpy/bugzilla.py:
+ * Scripts/webkitpy/commands/download.py:
+ * Scripts/webkitpy/commands/download_unittest.py:
+ * Scripts/webkitpy/steps/createbug.py:
+
+2010-03-17 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add a way for the bots to send messages to IRC
+ https://bugs.webkit.org/show_bug.cgi?id=36235
+
+ We'll use these classes to notify #webkit about bad SVN revisions.
+ This patch just has some skeleton code for us to play with.
+
+ * Scripts/webkitpy/irc/__init__.py: Added.
+ * Scripts/webkitpy/irc/ircbot.py: Added.
+ A bot that knows how to talk to IRC.
+ * Scripts/webkitpy/irc/ircproxy.py: Added.
+ We need to run the bot on its own thread because the irclib needs
+ its own mainloop. This class provides an abstraction of the
+ threading.
+ * Scripts/webkitpy/irc/messagepump.py: Added.
+ * Scripts/webkitpy/irc/messagepump_unittest.py: Added.
+ * Scripts/webkitpy/irc/threadedmessagequeue.py: Added.
+ A thread-safe message queue for sending messages from the main
+ thread to the IRC thread.
+ * Scripts/webkitpy/irc/threadedmessagequeue_unittest.py: Added.
+ * Scripts/webkitpy/irc/unittests.py: Added.
+ * Scripts/webkitpy/thirdparty/autoinstalled/__init__.py:
+ Autoinstall irclib
+ * Scripts/webkitpy/unittests.py:
+
+2010-03-17 Victor Wang <victorw@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Fix image_diff syntax in webkitpy/port/base.py.
+ The syntax is wrong if diff_filename is specified.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36230
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+
+2010-03-16 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ Reviewed by Kenneth Christiansen.
+
+ Provide to QtLauncher a way to change the ViewportUpdateMode
+ when it's in graphics based mode.
+
+ [Qt] Make QtLaucher able to select the ViewportUpdateMode
+ https://bugs.webkit.org/show_bug.cgi?id=36175
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::selectViewportUpdateMode):
+ (LauncherWindow::createChrome):
+
+2010-03-17 Zoltan Horvath <zoltan@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ Ambiguous error message when building for unspecified platform
+ https://bugs.webkit.org/show_bug.cgi?id=30203
+
+ Add an extra line information to the error message.
+
+ * Scripts/webkitdirs.pm:
+
+2010-03-16 Adam Barth <abarth@webkit.org>
+
+ No review, rolling out r56044.
+ http://trac.webkit.org/changeset/56044
+ https://bugs.webkit.org/show_bug.cgi?id=36048
+
+ This patch broke Windows Debug (Tests)
+
+ * Scripts/webkitdirs.pm:
+
+2010-03-16 John Abd-El-Malek <jam@chromium.org>
+
+ Reviewed by Darin Fisher.
+
+ Give keyboard focus to PluginDocuments by default
+ https://bugs.webkit.org/show_bug.cgi?id=36147
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (pluginAllocate):
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h:
+ * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp:
+ (NPP_New):
+ (NPP_SetWindow):
+ (handleEventCarbon):
+ (handleEventCocoa):
+
+2010-03-16 Sam Weinig <sam@webkit.org>
+
+ Reviewed by Mark Rowe.
+
+ Fix run-webkit-httpd on Windows.
+
+ * Scripts/webkitperl/httpd.pm:
+
+2010-03-16 Alexey Proskuryakov <ap@apple.com>
+
+ Tiger build fix.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: (pluginGetProperty):
+ Added more type casts to shut down warnings.
+
+2010-03-16 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Darin Adler.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36184
+ YouTube video resizing doesn't work with OOP plug-ins
+
+ Added a resizeTo() method, which calls resizePlugin() in JS with the same arguments,
+ and a lastSetWindowArguments property, which returns a string describing the last NPWindow
+ passed to NPN_SetWindow.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (pluginGetProperty):
+ (testResizeTo):
+ (pluginInvoke):
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h:
+ * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp:
+ (NPP_SetWindow):
+
+2010-03-16 Joanmarie Diggs <joanmarie.diggs@gmail.com>
+
+ Reviewed by Xan Lopez.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35504
+ [Gtk] Evaluate and fix AtkTable for layout tables
+
+ Implements rowCount and columnCount for Gtk in DRT.
+
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::rowCount):
+ (AccessibilityUIElement::columnCount):
+
+2010-03-15 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Add "what-broke" command for debugging when the tree broke
+ https://bugs.webkit.org/show_bug.cgi?id=36157
+
+ This is another step towards automated sheriffing of the webkit tree.
+ With this logic our scripts are able to determine what revision broke the
+ tree. Buildbot should do this for us, but unfortunately buildbot doesn't
+ expose this kind of aggregate information.
+
+ * Scripts/webkitpy/buildbot.py:
+ - Add new Builder and Build classes (which will eventually replace the custom dictionaries previously used).
+ - Split out more network logic into _fetch methods which will eventually be their own class for mocking.
+ - Use XMLRPC to communicate with the buildbot master instead of scraping build pages.
+ * Scripts/webkitpy/buildbot_unittest.py:
+ - Test the newly added buildbot classes.
+ * Scripts/webkitpy/commands/queries.py:
+ - Add an experimental what-broke command.
+
+2010-03-15 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Eric Seidel.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36048
+
+ Detect if the Windows Platform SDK is missing when building with
+ Visual C++ Express Edition and inform the user to download it.
+
+ * Scripts/webkitdirs.pm:
+
+2010-03-15 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Let commit-queue land rollout patches even when the tree is red
+ https://bugs.webkit.org/show_bug.cgi?id=36155
+
+ Now the commit-queue will land patches whose name begins with "ROLLOUT "
+ even if the tree is red. The patches still go through the usual build
+ and test process, but they can be landed while the tree is on fire.
+
+ * Scripts/webkitpy/bugzilla.py:
+ * Scripts/webkitpy/commands/queues.py:
+ * Scripts/webkitpy/commands/queues_unittest.py:
+ * Scripts/webkitpy/commands/queuestest.py:
+ * Scripts/webkitpy/mock_bugzillatool.py:
+
+2010-03-15 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Add webkit-patch post-rollout to upload rollouts to bugs.webkit.org for easy committing
+ https://bugs.webkit.org/show_bug.cgi?id=36154
+
+ This new command is a mashup of prepare-rollout and post. This command
+ will be used by an experimental bot to post rollouts of patches that
+ break things to bugs.webkit.org where they can be landed with the
+ greatest of ease.
+
+ * Scripts/webkitpy/commands/download.py:
+ * Scripts/webkitpy/commands/download_unittest.py:
+ * Scripts/webkitpy/steps/__init__.py:
+ * Scripts/webkitpy/steps/postdiffforrevert.py: Added.
+
+2010-03-15 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ webkit-patch rollout should error out on conflicts
+ https://bugs.webkit.org/show_bug.cgi?id=36151
+
+ Instead of blindingly plowing ahead, we now throw an exception if there
+ are conflicts after applying a reverse diff.
+
+ * Scripts/webkitpy/scm.py:
+
+2010-03-15 Chris Fleizach <cfleizach@apple.com>
+
+ Unreviewed layout test fix.
+
+ VO not able to perform a VO-spacebar on facebook links
+ https://bugs.webkit.org/show_bug.cgi?id=36132
+
+ GTK needs to implement press for this test to work.
+
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::press):
+
+2010-03-15 Chris Fleizach <cfleizach@apple.com>
+
+ Unreviewed layout test fix.
+
+ VO not able to perform a VO-spacebar on facebook links
+ https://bugs.webkit.org/show_bug.cgi?id=36132
+
+ Windows needs to implement press in DRT.
+
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::press):
+
+2010-03-15 Chris Fleizach <cfleizach@apple.com>
+
+ Unreviewed. Fix break of layout tests on win and gtk.
+
+ VO not able to perform a VO-spacebar on facebook links
+ https://bugs.webkit.org/show_bug.cgi?id=36132
+
+ Attempting to implement press action for windows and gtk.
+
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::press):
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::press):
+
+2010-03-15 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Beth Dakin.
+
+ VO not able to perform a VO-spacebar on facebook links
+ https://bugs.webkit.org/show_bug.cgi?id=36132
+
+ * DumpRenderTree/AccessibilityUIElement.cpp:
+ (pressCallback):
+ (AccessibilityUIElement::getJSClass):
+ * DumpRenderTree/AccessibilityUIElement.h:
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::press):
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+ (AccessibilityUIElement::press):
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::press):
+
+2010-03-15 Mark Rowe <mrowe@apple.com>
+
+ Add a new build slave to replace the existing SnowLeopard Leaks build slave which
+ appears to be suffering a slow and painful death at the hands of its graphics hardware.
+
+ * BuildSlaveSupport/build.webkit.org-config/config.json:
+
+2010-03-15 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Alexey Proskuryakov.
+
+ Fix a minor case where we'd deference a null pointer if we tried
+ to run new-run-webkit-tests on an unsupported platform (e.g.
+ Cygwin's python version).
+
+ https://bugs.webkit.org/show_bug.cgi?id=36076
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+
+2010-03-15 Darin Adler <darin@apple.com>
+
+ Tell Subversion about more directories that expect to have .pyc files.
+
+ * Scripts/webkitpy/layout_tests: Added property svn:ignore.
+ * Scripts/webkitpy/layout_tests/port: Added property svn:ignore.
+
+2010-03-15 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Darin Adler.
+
+ Operational scripts from running the EWS
+ https://bugs.webkit.org/show_bug.cgi?id=36097
+
+ These are the scripts I use to manage the EWS on EC2. If someone other
+ than me wants to run the EWS, these scripts might be helpful.
+
+ * EWSTools/boot.sh: Added.
+ * EWSTools/create-webkit-git: Added.
+ * EWSTools/screen-config: Added.
+ * EWSTools/start-queue.sh: Added.
+
+2010-03-14 Adam Barth <abarth@webkit.org>
+
+ Unreviewed.
+
+ Fix the webkit-patch bots. Turns out they need the path of the main
+ script to run properly.
+
+ * Scripts/webkit-patch:
+ * Scripts/webkitpy/patch/patcher.py:
+
+2010-03-14 Darin Adler <darin@apple.com>
+
+ Tell Subversion about more directories that expect to have .pyc files.
+
+ * Scripts/webkitpy/init: Added property svn:ignore.
+ * Scripts/webkitpy/patch: Added property svn:ignore.
+ * Scripts/webkitpy/thirdparty: Added property svn:ignore.
+ * Scripts/webkitpy/thirdparty/autoinstalled: Added property svn:ignore.
+
+2010-03-14 Antti Koivisto <koivisto@iki.fi>
+
+ Reviewed by Simon Hausmann.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35146
+ Support tiled backing store
+
+ QtLauncher support and build flag in build-webkit.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::applyPrefs):
+ (LauncherWindow::toggleTiledBackingStore):
+ (LauncherWindow::toggleResizesToContents):
+ (LauncherWindow::createChrome):
+ (LauncherApplication::handleUserOptions):
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::WebViewGraphicsBased):
+ (WebViewGraphicsBased::setResizesToContents):
+ (WebViewGraphicsBased::resizeEvent):
+ * QtLauncher/webview.h:
+ * Scripts/build-webkit:
+
+2010-03-13 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ The webkit-patch script now displays a warning if run using
+ a version of Python less than 2.5. This will help users
+ understand why webkit-patch is erroring out if they are
+ using Python 2.4, for example.
+
+ https://bugs.webkit.org/show_bug.cgi?id=31533
+
+ * Scripts/webkit-patch:
+ - Moved most of the file contents to webkitpy/patch/patcher.py
+ so the Python version can be checked before interpreting
+ any code that can cause the script to error out.
+ - Added a configure_logging() method to enable any version
+ warnings to show up.
+ - Added a main() method with calls to configure_logging(),
+ check_version(), and the main webkit patch method.
+
+ * Scripts/webkitpy/patch/__init__.py: Copied from WebKitTools/QueueStatusServer/filters/__init__.py.
+ - This file is required to make a folder a package.
+
+ * Scripts/webkitpy/patch/patcher.py: Added.
+ - Moved code from Scripts/webkit-patch.
+
+2010-03-13 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Changed test-webkitpy so that messages logged as a side-effect
+ of unit-testing code do not get displayed to the screen. These
+ messages clutter up the unit test results if not filtered out.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35835
+
+ * Scripts/test-webkitpy:
+ - Adjusted the configure_logging() method to filter out any
+ log messages from webkitpy.
+ - Also added an INFO message stating that most console logging
+ is getting suppressed.
+
+ * Scripts/webkitpy/init/versioning.py:
+ - Added a log parameter to the check_version() method.
+
+ * Scripts/webkitpy/init/versioning_unittest.py:
+ - Qualified a call to check_version() with the parameter names.
+
+2010-03-13 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ The test-webkitpy script now warns the user if the script is
+ being run using a Python version different from the minimum
+ version the webkitpy package was meant to support.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35788
+
+ Warning developers if their Python version is too low will help
+ them understand why test-webkitpy is failing. Secondly, warning
+ developers if their Python version is higher than the minimum will
+ help them understand that their changes may not be okay for the
+ minimum supported version, even if test-webkitpy is passing.
+
+ * Scripts/test-webkitpy:
+ - Moved the "from ..._unittest import *" lines to the new
+ file Scripts/webkitpy/unittests.py. This will allow the
+ version-check warning to be displayed even if an error occurs
+ while interpreting (i.e. importing) the unit test code.
+ - Added configure_logging() to configur logging for test-webkitpy.
+ - Added an init() method to configure logging and check the
+ current Python version.
+
+ * Scripts/webkitpy/init/unittests.py: Added.
+ - Added a file to import all unit test modules in the
+ webkitpy.init package.
+
+ * Scripts/webkitpy/init/versioning.py: Added.
+ - Added a _MINIMUM_SUPPORTED_PYTHON_VERSION variable and set
+ it equal to 2.5.
+ - Added a compare_version() method to compare the current Python
+ version against a target version.
+ - Added a check_version() method to check the current Python
+ version against the current minimum supported version, and to
+ log a warning message if the check fails.
+
+ * Scripts/webkitpy/init/versioning_unittest.py: Added.
+ - Added unit tests for the functions in versioning.py.
+
+ * Scripts/webkitpy/style/unittests.py:
+ - Fixed/updated a code comment.
+
+ * Scripts/webkitpy/unittests.py: Added.
+ - Moved the "from ..._unittest import *" lines from test-webkitpy.
+
+2010-03-13 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved all code in webkitpy/__init__.py to another location.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35828
+
+ Keeping webkitpy/__init__.py free of non-trivial code allows
+ calling code to import initialization code from webkitpy
+ before any errors or log messages occur due to code in
+ __init__.py. Such initialization code can include things like
+ version checking code and logging configuration code. This
+ also lets us move the autoinstall initialization code to a
+ location where it only executes if it is needed -- something
+ we have done in this patch.
+
+ * Scripts/webkitpy/__init__.py:
+ - Moved all executable code to the following location:
+ webkitpy/thirdparty/autoinstalled/__init__.py
+ - Added a code comment to keep this file free of non-trivial
+ code.
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/networktransaction.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/networktransaction_unittest.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/statusserver.py:
+ - Updated mechanize import statement.
+
+ * Scripts/webkitpy/thirdparty/autoinstalled/__init__.py: Added.
+ - Copied the code from webkitpy/__init__.py and updated it
+ as necessary.
+
+2010-03-13 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved webkitpy/mock.py into webkitpy/thirdparty since it is
+ third-party code.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35499
+
+ Updated the import statement in all of the below except where noted.
+
+ * Scripts/webkitpy/bugzilla_unittest.py:
+ * Scripts/webkitpy/commands/commandtest.py:
+ * Scripts/webkitpy/commands/download_unittest.py:
+ * Scripts/webkitpy/commands/early_warning_system_unittest.py:
+ * Scripts/webkitpy/commands/queries_unittest.py:
+ * Scripts/webkitpy/commands/queues_unittest.py:
+ * Scripts/webkitpy/commands/queuestest.py:
+ * Scripts/webkitpy/commands/upload.py:
+ * Scripts/webkitpy/commands/upload_unittest.py:
+ * Scripts/webkitpy/commands_references.py: Added.
+ - Added a file containing an absolute import of Mock so that
+ the imports in the commands folder can import from this file
+ (similar to style_references.py). This helps limit the
+ number of affected files in future refactorings.
+
+ * Scripts/webkitpy/credentials_unittest.py:
+ * Scripts/webkitpy/mock.py: Removed.
+ - Moved to Scripts/webkitpy/thirdparty.
+
+ * Scripts/webkitpy/mock_bugzillatool.py:
+ * Scripts/webkitpy/patchcollection_unittest.py:
+ * Scripts/webkitpy/steps/closebugforlanddiff_unittest.py:
+ * Scripts/webkitpy/steps/steps_unittest.py:
+ * Scripts/webkitpy/steps/updatechangelogswithreview_unittests.py:
+ * Scripts/webkitpy/steps_references.py: Added.
+ - Added a file containing an absolute import of Mock so that
+ the imports in the steps folder can import from this file
+ (similar to style_references.py). This helps limit the
+ number of affected files in future refactorings.
+
+ * Scripts/webkitpy/thirdparty/mock.py: Copied from WebKitTools/Scripts/webkitpy/mock.py.
+ - Also eliminated trailing white space and carriage returns.
+
+2010-03-12 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Amend incorrect typo patch for QtLauncher.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35877
+
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::setFrameRateMeasurementEnabled):
+
+2010-03-13 Victor Wang <victorw@chromium.org>
+
+ Add appengine app to host and serve webkit layout test results.
+
+ The app allows you post test result files (json) and serve them up.
+ Chromium flakiness dashboard will first use this app to host results.json
+ and expectations.json, but the files hosted by this app are not limited
+ to chromium results or json files. It can be used to host other files if needed.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35944
+
+ * TestResultServer: Added.
+ * TestResultServer/app.yaml: Added.
+ * TestResultServer/handlers: Added.
+ * TestResultServer/handlers/__init__.py: Added.
+ * TestResultServer/handlers/menu.py: Added.
+ * TestResultServer/handlers/testfilehandler.py: Added.
+ * TestResultServer/index.yaml: Added.
+ * TestResultServer/main.py: Added.
+ * TestResultServer/model: Added.
+ * TestResultServer/model/__init__.py: Added.
+ * TestResultServer/model/testfile.py: Added.
+ * TestResultServer/stylesheets: Added.
+ * TestResultServer/stylesheets/form.css: Added.
+ * TestResultServer/stylesheets/menu.css: Added.
+ * TestResultServer/stylesheets/testfile.css: Added.
+ * TestResultServer/templates: Added.
+ * TestResultServer/templates/menu.html: Added.
+ * TestResultServer/templates/showfilelist.html: Added.
+ * TestResultServer/templates/uploadform.html: Added.
+
+2010-03-13 Dimitri Glazkov <dglazkov@chromium.org>
+
+ Reviewed by David Levin.
+
+ new-run-webkit-tests fails with --debug option.
+ https://bugs.webkit.org/show_bug.cgi?id=36067
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-13 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add "Show FPS" menu option to QtLauncher.
+
+ [Qt] QtLauncher need a menu option to show/hide FPS
+ https://bugs.webkit.org/show_bug.cgi?id=35794
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::showFPS):
+ (LauncherWindow::createChrome):
+
+2010-03-13 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Add a "Toggle FullScreen" option to QtLauncher Menu.
+
+ [Qt] QtLauncher needs an option to toggle FullScreen Mode
+ https://bugs.webkit.org/show_bug.cgi?id=35755
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::init):
+ (LauncherWindow::eventFilter):
+ (LauncherWindow::initializeView):
+ (LauncherWindow::toggleFullScreenMode):
+ (LauncherWindow::createChrome):
+
+2010-03-12 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Fix typo in websocket_server (path_from_base instead of
+ path_from_chromium_base).
+
+ https://bugs.webkit.org/show_bug.cgi?id=36074
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-03-12 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ new-run-webkit-tests --new-baseline doesn't work at all.
+
+ It attempts to call a method that isn't defined. To fix it, I
+ removed the unnecessary and unnecessarily confusing 'platform'
+ argument to the test_type constructor and use the Port object that
+ is passed in instead, since we are only ever generating a baseline
+ from the port that is currently executing.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36046
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+
+2010-03-12 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Fix new-run-webkit-tests --run-singly
+
+ This script option is currently broken - the script attempts to
+ dereference methods and variables that don't exist, which causes
+ the Chromium Linux valgrind bot to be quite unhappy. This has been
+ broken since r54449 when I renamed Port.start_test_driver to
+ Port.start_driver.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36042
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py:
+
+2010-03-12 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Fix critical being printed to stderr on every test. This is
+ because the jar is only being created when soup hits the HTTP
+ path. We should reconsider the time of its creation.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+
+2010-03-12 Adam Roben <aroben@apple.com>
+
+ Teach prepare-ChangeLog to find modified selectors in CSS files
+
+ Reviewed by Tim Hatcher.
+
+ Fixes <http://webkit.org/b/36064> prepare-ChangeLog should extract
+ modified selectors from CSS files
+
+ * Scripts/prepare-ChangeLog:
+ (get_function_line_ranges): Call get_selector_line_ranges_for_css for
+ .css files.
+ (get_selector_line_ranges_for_css): Added. Finds selectors and their
+ line ranges and returns them.
+
+2010-03-12 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Build fix (for EWS). Make sure the new code builds on older soup.
+
+ Thanks to Dan Winship.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setAlwaysAcceptCookies):
+
+2010-03-12 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Reviewed by Eric Carlson.
+
+ [GTK] DRT does not handle cookie policy setting
+ https://bugs.webkit.org/show_bug.cgi?id=36056
+
+ Implement cookie accept policy setting for GTK+'s LayoutTestController.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setAlwaysAcceptCookies):
+
+2010-03-12 Adam Langley <agl@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [chromium]: update Linux layout test scripts for RedHat like systems.
+
+ (Tested on Fedora 12.)
+
+ https://bugs.webkit.org/show_bug.cgi?id=35867
+
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+
+2010-03-12 Adam Roben <aroben@apple.com>
+
+ Make svn-create-patch's diffs of ObjC header files more readable
+
+ Fixes <http://webkit.org/b/36055>.
+
+ Reviewed by John Sullivan.
+
+ * Scripts/svn-create-patch:
+ (diffOptionsForFile): Added. Returns the options that should be passed
+ to diff for the given file. All the options are the same for all
+ files, except for the option to specify which lines should be used as
+ hunk headers.
+ (generateDiff): Use diffOptionsForFile to get the options to pass to
+ diff.
+ (hunkHeaderLineRegExForFile): Added. Returns the regular expression
+ that should be used by diff to identify lines that should be included
+ after the "@@" in the hunk header lines of the diff. For ObjC[++]
+ source files, we use any lines starting with -, +, or
+ @implementation/@interface/@protocol. For ObjC[++] header files (which
+ we assume to be any .h files in a mac/ or objc/ directory), we use any
+ lines starting with @implementation/@interface/@protocol.
+
+2010-03-12 Jochen Eisinger <jochen@chromium.org>
+
+ Reviewed by Jeremy Orlow.
+
+ Introduce setWillSendRequestClearHeader to LayoutTestController to selectively remove headers in willSendRequest. Used in http/tests/security/no-referrer.html
+ https://bugs.webkit.org/show_bug.cgi?id=35920
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (setWillSendRequestClearHeaderCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ (LayoutTestController::willSendRequestClearHeaders):
+ (LayoutTestController::setWillSendRequestClearHeader):
+ * DumpRenderTree/mac/ResourceLoadDelegate.mm:
+ (-[ResourceLoadDelegate webView:resource:willSendRequest:redirectResponse:fromDataSource:]):
+ * DumpRenderTree/win/ResourceLoadDelegate.cpp:
+ (ResourceLoadDelegate::willSendRequest):
+
+2010-03-11 Fumitoshi Ukai <ukai@chromium.org>
+
+ Unreviewed.
+
+ Fix typo in websocket_server.py
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-03-11 Garret Kelly <gdk@chromium.org>
+
+ Reviewed by Darin Adler.
+
+ Fixing minor typo in the commit queue status page.
+ https://bugs.webkit.org/show_bug.cgi?id=35979
+
+ * Scripts/webkitpy/commands/queues.py:
+
+2010-03-11 Fumitoshi Ukai <ukai@chromium.org>
+
+ Unreviewed.
+
+ Fix for WebSocket layout test runner on chromium/win port.
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py: register_cygwin and set CYGWIN_PATH
+
+2010-03-11 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by David Kilzer.
+
+ <rdar://problem/7745082> Make it possible to build WebKit for older Mac OS X versions from the current Mac OS X version
+
+ Default to using the appropriate SDK if the target Mac OS X version is not the current Mac OS X version.
+
+ * DumpRenderTree/mac/Configurations/Base.xcconfig:
+
+2010-03-11 Victor Wang <victorw@chromium.org>
+
+ Reviewed by dglazkov@chromium.org.
+
+ rebaseline_chromium_webkit_tests can generate new baselines for
+ all platforms so it needs to know two ports in order to work correctly:
+ the port that the script is running on and the port that it generates
+ new baselines for. Update rebaselining tool to handle both port correctly.
+
+ https://bugs.webkit.org/show_bug.cgi?id=36032
+
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-03-11 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by Tim Hatcher.
+
+ <rdar://problem/7745082> Make it possible to build WebKit for older Mac OS X versions from the current Mac OS X version
+
+ Introduce TARGET_MAC_OS_X_VERSION_MAJOR to represent the Mac OS X version that is being targeted. It defaults to the
+ current Mac OS X version unless otherwise specified.
+
+ Key off TARGET_MAC_OS_X_VERSION_MAJOR where we'd previously been keying off MAC_OS_X_VERSION_MAJOR.
+
+ Explicitly map from the target Mac OS X version to the preferred compiler since Xcode's default compiler choice
+ may not be usable when targetting a different Mac OS X version.
+
+ Key off TARGET_GCC_VERSION rather than MAC_OS_X_VERSION_MAJOR in locations where we'd previously been keying off
+ MAC_OS_X_VERSION_MAJOR but the decision is really related to the compiler version being used.
+
+ * DumpRenderTree/mac/Configurations/Base.xcconfig:
+ * DumpRenderTree/mac/Configurations/DebugRelease.xcconfig:
+ * DumpRenderTree/mac/DumpRenderTree.mm: Wrap the include of mach-o/getsect.h in 'extern "C"' as some versions of the
+ header in older SDKs do not do this inside the header.
+
+2010-03-11 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Geoff Garen.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35965
+ <rdar://problem/7742771> Crash when passing an object returned from plug-in back to the plug-in
+
+ Made rememberedObject a member of PluginObject. A plug-in must not use its references
+ to browser NPObjects after being destroyed, but this wasn't the case with static variable.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (pluginInvoke):
+ (pluginInvalidate):
+ (pluginAllocate):
+ (pluginDeallocate):
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h:
+
+2010-03-11 Simon Fraser <simon.fraser@apple.com>
+
+ Reviewed by Oliver Hunt.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35905
+ REGRESSION(55699?): media/video-no-autoplay.html times out on Leopard Commit Bot
+
+ Make sure we reset the WebGL preference, so that WebGL doesn't get left
+ on after being enabled via layoutTestController.overridePreference(),
+ which in turn causes accelerated compositing to be enabled on Leopard
+ when we don't want it to be.
+
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetDefaultsToConsistentValues):
+
+2010-03-10 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ rebaseline_chromium_webkit_tests doesn't handle other plaforms
+ correctly (e.g., if you run on the Mac platform and try to
+ rebaseline the WIN results, the result gets written into
+ platform/mac instead of platform/chromium-win). Also, this script
+ doesn't work on non-Chromium ports, so we need to fix that at some
+ point.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35982
+
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+
+2010-03-10 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Simon Hausmann.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35840
+
+ Updates the subroutine builtDylibPathForName() so that it adds the "d" suffix to
+ the QtWebKit library name on Windows. This change makes run-launcher work again
+ under Windows. Moreover, this change corresponds to the change made in change-
+ set 53924 <http://trac.webkit.org/changeset/53924>.
+
+ * Scripts/webkitdirs.pm:
+
+2010-03-10 Adam Roben <aroben@apple.com>
+
+ Roll out the prepare-ChangeLog part of r55870
+
+ This change wasn't needed (prepare-ChangeLog calls svn-create-patch
+ when it's asked to print out diffs for the user) and was screwing up
+ its ability to find changed function names.
+
+ * Scripts/prepare-ChangeLog:
+ (diffCommand): Change the options we pass to svn-diff back to their
+ pre-r55870 form.
+
+2010-03-10 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Simon Hausmann.
+
+ [Qt] Add Support for WebKitEnableCaretBrowsing to Qt DRT
+
+ Unskip test fast/events/multiline-link-arrow-navigation.html
+
+ https://bugs.webkit.org/show_bug.cgi?id=35593
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::resetSettings):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::overridePreference):
+ (LayoutTestController::setCaretBrowsingEnabled):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+2010-03-10 Robert Hogan <robert@webkit.org>
+
+ Reviewed by Holger Freyther.
+
+ QtLauncher: Fix typo in conditional statement in
+ WebViewGraphicsBased::setFrameRateMeasurementEnabled.
+
+ '=' should be '=='!
+
+ https://bugs.webkit.org/show_bug.cgi?id=35877
+
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::setFrameRateMeasurementEnabled):
+
+2010-03-10 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Refactored and cleaned up the code for unit-testing logging.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35845
+
+ * Scripts/webkitpy/init/logtesting.py:
+ - Added more information to the module docstring.
+ - Added an assertMessages() method to the UnitTestLogStream
+ class. This simplifies the calling code.
+ - Renamed the UnitTestLog class to LogTesting, and reformulated
+ it as follows:
+ - Moved the logging configuration code from the __init__
+ method to a new static setUp() method.
+ - Documented the __init__ method to be private.
+ - Improved the code so that the root logger does not have
+ its logging level changed. Instead we set the handler's
+ level. This makes the unit testing more unintrusive.
+ - Updated the assertMessages() method to call the
+ UnitTestLogStream class's assertMessages() method.
+ - More fully documented the class.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added a logger parameter to the configure_logging() method.
+ This allows us to prevent test messages from being sent
+ to the root logger during unit testing, which may be
+ rendering to the screen, etc.
+ - Simplified the code by removing the _LevelLoggingFilter class.
+ - Replaced the _LevelLoggingFilter class with a one-line lambda
+ expression in configure_logging().
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Changed relative imports to absolute to comply more with PEP8.
+ - In the ConfigureLoggingTest class:
+ - Changed the setUp() method to prevent test messages from
+ being propagated to the root logger.
+ - Changed the _log() method to a data attribute.
+ - Updated to accommodate changes to logtesting.py.
+
+ * Scripts/webkitpy/style_references.py:
+ - Updated an import statement.
+
+2010-03-10 Evan Martin <evan@chromium.org>
+
+ Reviewed by Darin Adler.
+
+ Python code for GDB 7 to support native printing of some WebCore types.
+
+ * gdb/webcore.py: Added.
+
+2010-03-10 Adam Roben <aroben@apple.com>
+
+ Make svn-create-patch and prepare-ChangeLog show better section
+ headings for ObjC files
+
+ This makes the text at the end of each "@@" line in a diff actually
+ show the ObjC method or interface that contains the change, rather
+ than whatever the most-recently-defined C function was.
+
+ Fixes <http://webkit.org/b/35970>.
+
+ Reviewed by John Sullivan.
+
+ * Scripts/svn-create-patch: Pass -F'^[-+@]' to diff so that it will
+ treat any lines starting with -, +, or @ as section heading lines.
+ This works well for ObjC files, and shouldn't affect other types of
+ files.
+
+ * Scripts/prepare-ChangeLog: Changed the options passed to diff to
+ match those used in svn-create-patch.
+
+2010-03-10 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Reviewed by Tor Arne Vestbø.
+
+ Link QtLauncher against the WebKit library using a relative rpath.
+
+ This makes the launcher and the lib relocatable.
+
+ * QtLauncher/QtLauncher.pro:
+
+2010-03-10 Holger Hans Peter Freyther <zecke@selfish.org>
+
+ Rubber-stamped by Simon Hausmann.
+
+ [iExploder] Add new CSS Properties and HTML Attributes
+
+ The update-iexploder-cssproperties script was used to update
+ the various input files. The autobuffer HTML Attribute was removed
+ from WebKit and I manually added it back to the htmlattrs.in like
+ we have done it for other attributes in the past.
+
+ * iExploder/htdocs/cssproperties.in:
+ * iExploder/htdocs/htmlattrs.in:
+ * iExploder/htdocs/htmltags.in:
+
+2010-03-09 Fumitoshi Ukai <ukai@chromium.org>
+
+ Unreviewed.
+
+ Obvious fix for --cgi-paths of pywebsocket.
+
+ * Scripts/run-webkit-websocketserver:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-03-09 Jakub Wieczorek <jwieczorek@webkit.org>
+
+ Unreviewed.
+
+ Adding myself to committers.py.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-03-09 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Nate Chapin.
+
+ Fix --clobber-old-results in new-run-webkit-tests.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35778
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-03-09 Andy Estes <aestes@apple.com>
+
+ Reviewed by Adele Peterson.
+
+ Add the ability to dispatch scroll wheel events in DRT. This was
+ necessary in order to write a test for
+ https://bugs.webkit.org/show_bug.cgi?id=34700.
+
+ * DumpRenderTree/mac/EventSendingController.mm: Add support for two
+ new methods to EventSender: mouseScrollBy(x, y) and
+ continuousMouseScrollBy(x, y). The API to generate scroll events on
+ the mac was added in 10.5, so these methods are NOOPs on Tiger.
+ (+[EventSendingController isSelectorExcludedFromWebScript:]):
+ Regiester mouseScrollByX:andY: and continuousMouseScrollByX:andY:
+ (+[EventSendingController webScriptNameForSelector:]): Map JavaScript
+ method names to ObjC selectors.
+ (-[EventSendingController mouseScrollByX:andY:continuously:]): Generate
+ a scroll wheel event using CGEventCreateScrollWheelEvent() and dispatch
+ it to WebKit.
+ (-[EventSendingController continuousMouseScrollByX:andY:]): Generate a
+ continuous scrolling event by x and y pixels.
+ (-[EventSendingController mouseScrollByX:andY:]): Generate a notchy
+ scrolling event by x and y lines.
+
+2010-03-09 Chris Fleizach <cfleizach@apple.com>
+
+ DRT build fix for Tiger. No review.
+
+ AX: hit testing a list box doesn't work anymore
+ https://bugs.webkit.org/show_bug.cgi?id=35893
+
+ * DumpRenderTree/mac/AccessibilityControllerMac.mm:
+ (AccessibilityController::elementAtPoint):
+
+2010-03-09 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Darin Adler.
+
+ AX: hit testing a list box doesn't work anymore
+ https://bugs.webkit.org/show_bug.cgi?id=35893
+
+ * DumpRenderTree/AccessibilityController.cpp:
+ (getElementAtPointCallback):
+ (AccessibilityController::getJSClass):
+ * DumpRenderTree/AccessibilityController.h:
+ * DumpRenderTree/gtk/AccessibilityControllerGtk.cpp:
+ (AccessibilityController::elementAtPoint):
+ * DumpRenderTree/mac/AccessibilityControllerMac.mm:
+ (AccessibilityController::elementAtPoint):
+ * DumpRenderTree/win/AccessibilityControllerWin.cpp:
+ (AccessibilityController::elementAtPoint):
+
+2010-03-03 Fumitoshi Ukai <ukai@chromium.org>
+
+ Reviewed by Alexey Proskuryakov.
+
+ pywebsocket should support html and cgi in the same directory.
+ https://bugs.webkit.org/show_bug.cgi?id=34879
+
+ Import pywebsocket 0.4.9.2
+ Specify --server-host 127.0.0.1, so that it only binds listening socket
+ to 127.0.0.1 to prevent access from non-localhost.
+ Change --cgi-paths from /websocket/tests/cookies to /websocket/tests,
+ because pywebsocket 0.4.9.2 supports html and cgi in the same directory
+ and only executable (httponly-cookies.pl) will be handled as cgi
+ script.
+
+ * Scripts/run-webkit-tests:
+ * Scripts/run-webkit-websocketserver:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/README.webkit:
+ * Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt: Added.
+ * Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/setup.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py:
+ * Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README: Added.
+ * Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl: Added.
+
+2010-03-09 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Simplified check-webkit-style's argument parsing code by removing
+ support for the vestigial "extra flag values" parameter.
+
+ https://bugs.webkit.org/show_bug.cgi?id=34677
+
+ The "extra flag values" parameter was needed before WebKit
+ forked check-webkit-style from Google. It was used to pass
+ through the option parser those command-line flags that WebKit
+ required but that Google's parser did not support (the --git-commit
+ flag in particular).
+ We can remove the parameter now because it is no longer
+ needed and unnecessarily clutters the argument-parsing code.
+
+ * Scripts/webkitpy/style/optparser.py:
+ - Removed the extra_flag_values parameter from the
+ CommandOptionValues class's constructor.
+ - Removed the extra_flags parameter from the ArgumentParser
+ class's parse() method.
+
+ * Scripts/webkitpy/style/optparser_unittest.py:
+ - Removed from the unit tests all references to the
+ extra_flag_values variable.
+
+2010-03-08 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add EventSender
+ https://bugs.webkit.org/show_bug.cgi?id=35859
+
+ Add EventSender classes, which are going to be used by
+ DumpRenderTree Chromium port. These files are based on:
+ - src/webkit/tools/test_shell/event_sending_controller.cc
+ - src/webkit/tools/test_shell/event_sending_controller.h
+ of Chromium rev.40492.
+
+ * DumpRenderTree/chromium/EventSender.cpp: Added.
+ * DumpRenderTree/chromium/EventSender.h: Added.
+
+2010-03-08 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add PlainTextController and TextInputController
+ https://bugs.webkit.org/show_bug.cgi?id=35852
+
+ Add PlainTextController and TextInputController classes, which are going
+ to be used by DumpRenderTree Chromium port. These files are based on:
+ - src/webkit/tools/test_shell/plain_text_controller.{cc,h} and
+ - src/webkit/tools/test_shell/text_input_controller.{cc,h}
+ of Chromium rev.40492.
+
+ * DumpRenderTree/chromium/PlainTextController.cpp: Added.
+ * DumpRenderTree/chromium/PlainTextController.h: Added.
+ * DumpRenderTree/chromium/TextInputController.cpp: Added.
+ * DumpRenderTree/chromium/TextInputController.h: Added.
+
+2010-03-08 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, Chromium build fix.
+
+ Reverting r55689.
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-03-08 Fumitoshi Ukai <ukai@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Fix webkit-patch post and land to work well with security bug
+ https://bugs.webkit.org/show_bug.cgi?id=35733
+
+ Bugzilla requires authentication to access security bug page,
+ so call authenticate() if it failed to fetch bug page.
+
+ * Scripts/webkitpy/bugzilla.py:
+
+2010-03-04 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitry Glazkov.
+
+ Fix --clobber-old-results in new-run-webkit-tests.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35778
+
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-03-08 Brady Eidson <beidson@apple.com>
+
+ Reviewed by NOBODY (but suggested by Steve Falkenburg and fixing a boneheaded mistake on my part last week)
+
+ Followup to https://bugs.webkit.org/show_bug.cgi?id=35532
+
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::apiTestGoToCurrentBackForwardItem): Can't pass in a null BOOL to WebKit APIs.
+
+2010-03-08 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Fixed incorrect import statement in validate-committer-lists:
+ webkitpy.BeautifulSoup -> webkitpy.thirdparty.BeautifulSoup.
+
+ * Scripts/validate-committer-lists:
+
+2010-03-08 Jian Li <jianli@chromium.org>
+
+ Reviewed by Dmitry Titov.
+
+ Blob.slice support.
+ https://bugs.webkit.org/show_bug.cgi?id=32993
+
+ Add ENABLE_BLOB_SLICE feature define and flag for build-webkit. It is
+ currently only turned on for Mac.
+
+ * Scripts/build-webkit:
+
+2010-03-08 Gustavo Noronha Silva <gns@gnome.org>
+
+ No review, rolling out r55662.
+ http://trac.webkit.org/changeset/55662
+ https://bugs.webkit.org/show_bug.cgi?id=35863
+
+ Need to be coordinated with bots setup
+
+ * Scripts/run-webkit-tests:
+
+2010-03-08 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Reviewed by Holger Freyther.
+
+ [GTK] Tests fail when running with ipv6 addresses available, on Debian systems
+ https://bugs.webkit.org/show_bug.cgi?id=35863
+
+ If running on a Debian-based system, also listen on the ipv6 address.
+
+ * Scripts/run-webkit-tests:
+
+2010-03-08 Holger Hans Peter Freyther <zecke@selfish.org>
+
+ Reviewed by Darin Adler.
+
+ [iexploder] Automatically update htmltags.in and htmlattrs.in too
+ https://bugs.webkit.org/show_bug.cgi?id=33755
+
+ Change the update-iexploder-cssproperites script to update
+ the htmlattrs.in and htmltags.in of WebKitTools/iExploder/htdocs
+ automatically as well.
+
+ Change the reading and writing code to work with parameters
+ and extend the method that is parsing the .in files to handle
+ the HTMLTagNames.in and the HTMLAttributeNames.in files.
+
+ Remove custom code to determine the revision of files with a
+ utility of VCUtils.pm to determine the revision of the directory
+ these files are located in. This will also work with git checkout.
+
+ * Scripts/update-iexploder-cssproperties:
+
+2010-03-07 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ check-webkit-style: false positive for empty loop
+ https://bugs.webkit.org/show_bug.cgi?id=35717
+
+ * Scripts/webkitpy/style/processors/cpp.py:
+ * Scripts/webkitpy/style/processors/cpp_unittest.py:
+
+2010-03-07 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add AccessibilityController and AccessibilityUIElement
+ https://bugs.webkit.org/show_bug.cgi?id=35774
+
+ Add AccessibilityController and AccessibilityUIElement classes,
+ which are going to be used by DumpRenderTree Chromium port. These
+ files are based on:
+ - src/webkit/tools/test_shell/accessibility_controller.{cc,h} and
+ - src/webkit/tools/test_shell/accessibility_ui_element.{cc,h}
+ of Chromium rev.40492.
+
+ * DumpRenderTree/chromium/AccessibilityController.cpp: Added.
+ * DumpRenderTree/chromium/AccessibilityController.h: Added.
+ * DumpRenderTree/chromium/AccessibilityUIElement.cpp: Added.
+ * DumpRenderTree/chromium/AccessibilityUIElement.h: Added.
+
+2010-03-06 Hironori Bono <hbono@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ [Chromium] Typing into Flash with wmode = opaque|transparent and
+ non-latin language active outputs as if US keyboard layout active
+
+ https://bugs.webkit.org/show_bug.cgi?id=34936
+
+ To test keyboard events on the test plugin, this change implements
+ NPCocoaEventKeyDown and NPCocoaEventKeyUp handlers so the plugin
+ can write log messages.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp:
+ (handleEventCocoa): Implemented the event handlers for NPCocoaKeyDown
+ and NPCocoaEventKeyUp.
+
+2010-03-05 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Eric Seidel.
+
+ Fix Print option on QtLauncher by calling print directly from QWebFrame.
+
+ [Qt] QtLauncher Print option is not working on QGraphicsView mode
+ https://bugs.webkit.org/show_bug.cgi?id=35769
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::print):
+
+2010-03-05 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Changed the logging code for new-run-webkit-tests to use
+ module-specific loggers rather than the root logger. This is
+ a standard practice that allows logging specific to a package
+ to be configured independently of other modules.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35194
+
+ Added a line of the form "_log = logging.getLogger(<module>)"
+ to each module below, where <module> is the fully-qualified
+ name of the module, and updated the log lines to use the new
+ _log logger.
+
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py:
+ * Scripts/webkitpy/layout_tests/port/apache_http_server.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/http_server.py:
+ * Scripts/webkitpy/layout_tests/port/http_server_base.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py:
+ * Scripts/webkitpy/layout_tests/test_types/test_type_base.py:
+ * Scripts/webkitpy/layout_tests/test_types/text_diff.py:
+
+2010-03-05 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Siedel.
+
+ Split the command-line invocation of the Chromium/python LigHTTPd
+ server implementation out into its own top level script to make it
+ a more "public" interface and to resolve some awkward layering
+ issues. This script will be called directly by other test scripts in
+ the Chromium tree.
+
+ At some point this script should be made to work with Apache-based
+ implementations and on other ports. I have filed
+ https://bugs.webkit.org/show_bug.cgi?id=35820 for this.
+
+ Also fix a bug in port/factory where options.chromium could be
+ dereferenced even if it wasn't set, raising an exception.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35812
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/http_server.py:
+ * Scripts/new-run-webkit-httpd: Added
+
+2010-03-02 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Holger Freyther
+ Patch by Antonio Gomes <tonikitoo@webkit.org>
+
+ [Gtk] Implement setSpatialNavigationEnabled in DRT.
+ https://bugs.webkit.org/show_bug.cgi?id=35705
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (resetDefaultsToConsistentValues):
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setSpatialNavigationEnabled):
+
+2010-02-23 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Kenneth Christiansen.
+ Patch by Antonio Gomes <tonikitoo@webkit.org>
+
+ Add toggle on/off stub methods for Spatial Navigation in win, gtk and mac LayoutTestController class implementations.
+ https://bugs.webkit.org/show_bug.cgi?id=35699
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (setSpatialNavigationEnabledCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::setSpatialNavigationEnabled):
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::setSpatialNavigationEnabled):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::setSpatialNavigationEnabled):
+
+2010-02-18 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Kenneth Christiansen.
+ Patch by Antonio Gomes <tonikitoo@webkit.org>
+
+ Add toggle on/off mechanism for Spatial Navigation in QtLauncher.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::toggleSpatialNavigation):
+ (LauncherWindow::setupUI):
+
+2010-03-02 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Simon Hausmann.
+ Patch by Antonio Gomes <tonikitoo@webkit.org>
+
+ [Qt] Add setSpatialNavigationEnabled method DRT
+ https://bugs.webkit.org/show_bug.cgi?id=33715
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::WebPage::WebPage):
+ (WebCore::WebPage::resetSettings):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp:
+ (LayoutTestController::setSpatialNavigationEnabled):
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+
+ Unskipped fast/events/spatialnavigation/
+
+2010-03-04 Mark Rowe <mrowe@apple.com>
+
+ Rubber-stamped by Alice Liu.
+
+ Find the framework relative to TARGET_BUILD_DIR as that has a more obvious value during production builds.
+
+ * Scripts/check-for-webkit-framework-include-consistency:
+
+2010-03-04 Kent Tamura <tkent@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ [DRT/Chromium] Add CppVariant and CppBoundClass
+ https://bugs.webkit.org/show_bug.cgi?id=35634
+
+ Add CppVariant and CppBoundClass classes, which are going to be
+ used by DumpRenderTree Chromium port. These files are based on:
+ - src/webkit/glue/cpp_variant.{cc,h} and
+ - src/webkit/glue/cpp_bound_class.{cc,h}
+ of Chromium rev.40492.
+
+ * DumpRenderTree/chromium/CppBoundClass.cpp: Added.
+ * DumpRenderTree/chromium/CppBoundClass.h: Added.
+ * DumpRenderTree/chromium/CppVariant.cpp: Added.
+ * DumpRenderTree/chromium/CppVariant.h: Added.
+
+2010-03-04 Mark Rowe <mrowe@apple.com>
+
+ Build fix for older versions of Ruby.
+
+ * Scripts/check-for-webkit-framework-include-consistency:
+
+2010-03-04 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by Sam Weinig.
+
+ Add a script to verify that WebKit framework headers are internally consistent.
+
+ This script detects the following problematic situations:
+ * An exported WebKit header that includes a header from WebCore.
+ * An exported WebKit header that includes a header that does not exist in the WebKit framework.
+ * A public WebKit header that includes a private WebKit header.
+
+ * Scripts/check-for-webkit-framework-include-consistency: Added.
+
+2010-03-04 Dumitru Daniliuc <dumi@chromium.org>
+
+ Reviewed by Adam Roben.
+
+ Making sure that the correct path is set before invoking
+ DumpRenderTree on cygwin.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35768
+
+ * Scripts/run-webkit-tests:
+
+2010-03-04 Simon Fraser <simon.fraser@apple.com>
+
+ Revert the exceptions I just added, and make the error clearer.
+
+ * Scripts/check-for-global-initializers:
+
+2010-03-04 Simon Fraser <simon.fraser@apple.com>
+
+ Build fix: add exceptions to the check-for-global-initializers script
+ for FocusController and SpatialNavigation, and improve the script
+ to actually print out the globals found.
+
+ * Scripts/check-for-global-initializers:
+
+2010-03-04 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Fix dangling reference to "port" instead of "self._port_obj" that
+ was preventing the http_server from starting on Windows.
+
+ * Scripts/webkitpy/layout_tests/port/http_server.py:
+
+2010-03-04 Diego Gonzalez <diego.gonzalez@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ Make Qt DumpRenderTree EventSender able to send double click events
+
+ LayoutTests:
+ fast/events/dblclick-addEventListener.html
+
+ [Qt] DRT: Send double click event from EventSender
+ https://bugs.webkit.org/show_bug.cgi?id=35255
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting):
+ * DumpRenderTree/qt/EventSenderQt.cpp:
+ (EventSender::EventSender):
+ (EventSender::mouseDown):
+ * DumpRenderTree/qt/EventSenderQt.h:
+ (EventSender::resetClickCount):
+
+2010-03-04 Jocelyn Turcotte <jocelyn.turcotte@nokia.com>
+
+ Reviewed by Tor Arne Vestbø.
+
+ [Qt] Make the OUTPUT_DIR variable in qmake projects independent of build-webkit's logic.
+
+ This also allows shadow builds relying only on qmake to work properly.
+
+ * DumpRenderTree/qt/DumpRenderTree.pro:
+ * DumpRenderTree/qt/ImageDiff.pro:
+ * DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro:
+ * QtLauncher/QtLauncher.pro:
+
+2010-03-04 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Enable to use built-product-archive on Qt platform.
+
+ * BuildSlaveSupport/built-product-archive:
+
+2010-03-03 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ Add a missing 'm_' to class variables names.
+
+ [Qt] QtLauncher is not respecting class variable names
+ https://bugs.webkit.org/show_bug.cgi?id=35542
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::LauncherWindow):
+ (LauncherWindow::init):
+ (LauncherWindow::sendTouchEvent):
+ (LauncherWindow::eventFilter):
+ (LauncherWindow::zoomIn):
+ (LauncherWindow::zoomOut):
+ (LauncherWindow::resetZoom):
+ (LauncherWindow::setEditable):
+ (LauncherWindow::setTouchMocking):
+ (LauncherWindow::initializeView):
+ (LauncherWindow::createChrome):
+
+2010-03-03 Alexey Proskuryakov <ap@apple.com>
+
+ Rubber-stamped by Mark Rowe.
+
+ Exclude leaks in Java that build bot complains about.
+
+ * Scripts/run-webkit-tests:
+
+2010-03-03 Chris Marrin <cmarrin@apple.com>
+
+ Reviewed by Simon Fraser.
+
+ Additional change to separate Accelerated Compositing test from 3D Rendering test
+ https://bugs.webkit.org/show_bug.cgi?id=35610
+
+ I am now using #if ENABLED(3D_RENDERING) to emit the 3DRendering string from
+ DRT or not. This allows these flags to set independently. I also improved
+ the code flow in run-webkit-tests a bit.
+
+ * DumpRenderTree/win/DumpRenderTree.cpp:
+ (main):
+ * Scripts/run-webkit-tests:
+
+2010-03-03 Chris Marrin <cmarrin@apple.com>
+
+ Reviewed by Simon Fraser.
+
+ Added ability to print supported features to console to DRT
+ https://bugs.webkit.org/show_bug.cgi?id=35610
+
+ This currently only prints whether or not Accelerated Compositing
+ and 3D Rendering are supported, which is the only way to tell if you
+ can run the compositing LayoutTests on Windows. But it can be expanded
+ to give more information as needed. Currently it prints that both
+ AcceleratedCompositing and 3DRendering are available if accelerated compositing
+ is enabled since both have to be turned on together. This allows me to maintain
+ separate checks for them.
+
+ * DumpRenderTree/win/DumpRenderTree.cpp:Added --print-supported-features flag
+ (main):
+ * Scripts/run-webkit-tests:Runs DRT and enabled compositing tests if HW comp is available on Windows
+
+2010-03-03 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Simon Hausmann.
+
+ Refactor QtLauncher so it respects command line arguments
+ and inherits settings when you clone or create a new window.
+
+ Implemented with help of Kenneth Rohde Christiansen.
+
+ [Qt] QtLauncher must be refactored to fix command line arguments usage
+ https://bugs.webkit.org/show_bug.cgi?id=35536
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::LauncherWindow):
+ (LauncherWindow::init):
+ (LauncherWindow::isGraphicsBased):
+ (applySetting):
+ (LauncherWindow::applyPrefs):
+ (LauncherWindow::initializeView):
+ (LauncherWindow::newWindow):
+ (LauncherWindow::cloneWindow):
+ (LauncherWindow::createChrome):
+ (main):
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::WebViewGraphicsBased):
+ (WebViewGraphicsBased::setFrameRateMeasurementEnabled):
+ * QtLauncher/webview.h:
+ (WebViewGraphicsBased::itemCacheMode):
+ (WebViewGraphicsBased::frameRateMeasurementEnabled):
+
+2010-03-02 Arno Renevier <arno@renevier.net>
+
+ Reviewed by Gustavo Noronha Silva.
+
+ [Gtk] implements ChromeClient::requestGeolocationPermissionForFrame
+ https://bugs.webkit.org/show_bug.cgi?id=35210
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (geolocationPolicyDecisionRequested):
+ (createWebView):
+
+2010-03-02 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by David Levin.
+
+ Revert r55339 - the Chromium codebase downstream had a temporary
+ need for WebKitTools/pywebsocket to still exist after Chris Jerdonek
+ had moved it (scripts still referenced the old location). Those
+ scripts have been updated to the new location, so it should be safe
+ to delete this now.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35614
+
+ * pywebsocket/mod_pywebsocket/standalone.py: Removed.
+
+2010-03-02 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ new-run-webkit-tests on chromium-linux tests to see if
+ layout_test_helper exists, but we don't use layout_test_helper on
+ linux. The test derefences a None object, and we crash. This fixes
+ that.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35602
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+ 2010-03-02 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ r55388 (bug 35553) worked around a bug in Python's subprocess.Popen()
+ that was causing DRT to hang on exit in new-run-webkit-tests.
+ Unfortunately, that workaround doesn't work on chromium-win
+ (and the script fails completely). The good news is that the check
+ isn't actually necessary, and so this change makes it conditional.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35601
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-03-02 Mark Rowe <mrowe@apple.com>
+
+ Reviewed by Darin Adler.
+
+ Bug 35576: WebKit should tell plug-in instances when private browsing state changes
+ <http://webkit.org/b/35576>
+
+ TestNetscapePlugin is another bit of plug-in code where copy-paste was heavily used
+ when porting. Update the Windows and UNIX implementations of NPP_New and NPP_SetValue
+ to provide the expected behavior related to NPNVprivateModeBool. Hopefully this code
+ duplication can be cleaned up in the future.
+
+ * DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp:
+ (webkit_test_plugin_new_instance):
+ (webkit_test_plugin_set_value):
+ * DumpRenderTree/win/TestNetscapePlugin/main.cpp:
+ (NPP_New):
+ (NPP_SetValue):
+
+2010-03-02 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Reviewed by Alexey Proskuryakov.
+
+ Test plugin still has issues with releasing objects, and variants
+ https://bugs.webkit.org/show_bug.cgi?id=35587
+
+ Fix the conditions for releasing the variants after calling
+ invoke, and avoid having a number of objects leak.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (testDocumentOpen):
+ (testWindowOpen):
+
+2010-03-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Refactored the StyleChecker class's file-processing method
+ _process_file(). This will make it easier to add new
+ file-processing capabilities to check-webkit-style.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35490
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added a _read_lines() method to the StyleChecker class
+ that extracts the lines from a file.
+ - Replaced part of _process_file() with a call to the new
+ _read_lines() method.
+ - Replaced another part of _process_file() with a call
+ to the new CarriageReturnProcessor.process() method.
+
+ * Scripts/webkitpy/style/processors/common.py:
+ - Replaced the check_no_carriage_return() function with a
+ new CarriageReturnProcessor class.
+
+ * Scripts/webkitpy/style/processors/common_unittest.py:
+ - Renamed the CarriageReturnTest class to
+ CarriageReturnProcessorTest and updated it as necessary.
+
+2010-03-02 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Started using the logging module in check-webkit-style.
+ This provides more options for debugging and a more flexible,
+ uniform way to report messages to the end-user.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35484
+
+ Also included classes in a central location to facilitate
+ the unit testing of logging code (setUp and tearDown of unit
+ test logging configurations, etc).
+
+ * Scripts/check-webkit-style:
+ - Added a call to configure_logging() in the beginning of main().
+ - Replaced two calls to sys.stderr.write() with appropriate
+ logging calls.
+
+ * Scripts/webkitpy/init/__init__.py: Copied from WebKitTools/QueueStatusServer/filters/__init__.py.
+
+ * Scripts/webkitpy/init/logtesting.py: Added.
+ - Added a UnitTestLogStream class to capture log output
+ during unit tests.
+ - Added a UnitTestLog class that provides convenience methods
+ for unit-testing logging code.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added a configure_logging() method.
+ - Added a _LevelLoggingFilter class to filter out log messages
+ above a certain logging level.
+ - Removed the _stderr_write() method from the StyleChecker class
+ and replaced its use with appropriate logging calls.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Added a ConfigureLoggingTest class to unit test the
+ configure_logging() method.
+ - Updated the StyleCheckerCheckFileTest class as necessary.
+
+ * Scripts/webkitpy/style_references.py:
+ - Added references to logtesting.UnitTestLog and
+ logtesting.UnitTestLogStream.
+
+2010-03-01 Chris Fleizach <cfleizach@apple.com>
+
+ Fixing broken DRT on Leopard/Tiger. Second try.
+
+ AX: changes to WAI-ARIA grid aren't perceived correctly by VoiceOver
+ https://bugs.webkit.org/show_bug.cgi?id=35514
+
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+
+2010-03-01 Chris Fleizach <cfleizach@apple.com>
+
+ Fixing broken DRT on Leopard/Tiger.
+
+ AX: changes to WAI-ARIA grid aren't perceived correctly by VoiceOver
+ https://bugs.webkit.org/show_bug.cgi?id=35514
+
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+
+2010-03-01 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Reviewed by Eric Seidel.
+
+ [GTK] plugins/setProperty.html fails on 64bit Release
+ https://bugs.webkit.org/show_bug.cgi?id=35425
+
+ Check invoke's return code before releasing the variant, since
+ there's a chance it won't be properly initialized, leading to
+ memory corruption, in some cases.
+
+ * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp:
+ (testCallback):
+ (testEnumerate):
+ (testDocumentOpen):
+ (testWindowOpen):
+ (handleCallback):
+
+2010-03-01 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by nobody. Build bustage :(
+
+ Fix stupid typo that I committed even after David Levin pointed
+ it out to me :(
+
+ https://bugs.webkit.org/show_bug.cgi?id=35553
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-01 Chris Fleizach <cfleizach@apple.com>
+
+ Reviewed by Darin Adler.
+
+ AX: changes to WAI-ARIA grid aren't perceived correctly by VoiceOver
+ https://bugs.webkit.org/show_bug.cgi?id=35514
+
+ Add rowCount, columnCount for tables.
+
+ * DumpRenderTree/AccessibilityUIElement.cpp:
+ (rowCountCallback):
+ (columnCountCallback):
+ (AccessibilityUIElement::getJSClass):
+ * DumpRenderTree/AccessibilityUIElement.h:
+ * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp:
+ (AccessibilityUIElement::rowCount):
+ (AccessibilityUIElement::columnCount):
+ * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+ (AccessibilityUIElement::rowCount):
+ (AccessibilityUIElement::columnCount):
+ * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+ (AccessibilityUIElement::rowCount):
+ (AccessibilityUIElement::columnCount):
+
+2010-03-01 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Work around a bug in Python's subprocess.Popen() that keeps us from
+ cleaning up DumpRenderTree / test_shell properly when we finish the
+ tests in new-run-webkit-tests.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35553
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+
+2010-03-01 Arno Renevier <arno@renevier.net>
+
+ Reviewed by Xan Lopez.
+
+ webkit-build could pass unknown arguments to autogen.sh
+ https://bugs.webkit.org/show_bug.cgi?id=35454
+
+ * Scripts/build-webkit:
+
+2010-03-01 Dirk Pranke <dpranke@chromium.org>
+
+ Rubber-stamped by Dimitri Glazkov.
+
+ Fix breakage from r55372.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35549
+
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+
+2010-03-01 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ EWS can hang for five hours when compile output is too big
+ https://bugs.webkit.org/show_bug.cgi?id=35545
+
+ * Scripts/webkitpy/commands/queues.py: Limit uploads to 1MB instead of
+ 5MB. AppEngine seems to not like 5MB uploads. I'm not sure what the
+ limit is. Let's try 1MB for a while and see how it goes.
+ * Scripts/webkitpy/networktransaction.py: Tune the default parameters
+ to our exponential backoff. I'm not sure why I picked five hours as
+ the retry limit. That seems way too large.
+
+2010-03-01 Brady Eidson <beidson@apple.com>
+
+ Reviewed by Sam Weinig.
+
+ REGRESSION: Telling a WebView to go to its current WebHistoryItem is broken.
+ <rdar://problem/7699371> and https://bugs.webkit.org/show_bug.cgi?id=35532
+
+ * DumpRenderTree/LayoutTestController.cpp:
+ (apiTestGoToCurrentBackForwardItemCallback):
+ (LayoutTestController::staticFunctions):
+ * DumpRenderTree/LayoutTestController.h:
+ * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+ (LayoutTestController::apiTestGoToCurrentBackForwardItem):
+
+ Stubs for now:
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::apiTestGoToCurrentBackForwardItem):
+ * DumpRenderTree/win/LayoutTestControllerWin.cpp:
+ (LayoutTestController::apiTestGoToCurrentBackForwardItem):
+ * DumpRenderTree/wx/LayoutTestControllerWx.cpp:
+ (LayoutTestController::apiTestGoToCurrentBackForwardItem):
+
+2010-03-01 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by David Levin.
+
+ General cleanup of error handling in new-run-webkit-tests.
+
+ Add Port.check_build() call that is separate from Port.check_sys_deps()
+ (and add a --nocheck-build flag to skip). This breaks a circular
+ dependency where you would start the layout test helper before
+ checking sys deps, but checking sys deps was the thing that told
+ you if your binaries where there.
+
+ Also, made Port.check_sys_deps(), start_helper() and stop_helper()
+ optional by providing default implementations in the base class
+ rather than requiring ports to implement the routines regardless
+ of whether or not they were needed.
+
+ Lastly, tweak a bunch of log messages to be cleaner, including
+ changing messages in thirdparty/autoinstall.py to be silent at
+ the default log level.
+
+ http://bugs.webkit.org/show_bug.cgi?id=35416
+
+ * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/passing.py:
+ * Scripts/webkitpy/layout_tests/port/test.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+ * Scripts/webkitpy/thirdparty/autoinstall.py
+
+2010-03-01 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by David Levin.
+
+ new-chromium-webkit-tests --platform=mac-leopard diffs are backwards
+ https://bugs.webkit.org/show_bug.cgi?id=35265
+
+ Some parts of the code passed arguments as
+ "actual, expected" and some passed as "expected, actual".
+ As you might imagine, this lead to great confusion and wrongness.
+ Standardize on "expected, actual" as that's the order which is
+ passed to the underlying diff tool.
+
+ Based on a patch by Eric Siedel <eric@webkit.org>.
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/test.py:
+ * Scripts/webkitpy/layout_tests/test_types/image_diff.py
+
+2010-03-01 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Rolling out: http://trac.webkit.org/changeset/55348
+
+ https://bugs.webkit.org/show_bug.cgi?id=35163
+
+ Rolling out since the changes to autoinstall do not work
+ with Python 2.4. In particular, ZipFile.extractall() was
+ added in Python 2.6.
+
+ * Scripts/webkitpy/__init__.py:
+ * Scripts/webkitpy/bugzilla.py:
+ * Scripts/webkitpy/init/__init__.py: Removed.
+ * Scripts/webkitpy/init/autoinstall.py: Removed.
+ * Scripts/webkitpy/networktransaction.py:
+ * Scripts/webkitpy/networktransaction_unittest.py:
+ * Scripts/webkitpy/statusserver.py:
+ * Scripts/webkitpy/thirdparty/autoinstall.py: Added.
+
+2010-03-01 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Unreviewed.
+
+ Rolling out r55350: http://trac.webkit.org/changeset/55350
+
+ https://bugs.webkit.org/show_bug.cgi?id=33639
+
+ Need to roll out because this patch (pep8) depends on the newly
+ rewritten autoinstall.py (r55348), which is breaking for people
+ with Python 2.4:
+
+ https://bugs.webkit.org/show_bug.cgi?id=35163#c21
+
+ That revision also needs to be rolled out and will be rolled out next.
+
+ * Scripts/webkitpy/style/checker.py:
+ * Scripts/webkitpy/style/checker_unittest.py:
+ * Scripts/webkitpy/style/processors/python.py: Removed.
+ * Scripts/webkitpy/style/processors/python_unittest.py: Removed.
+ * Scripts/webkitpy/style/processors/python_unittest_input.py: Removed.
+ * Scripts/webkitpy/style/unittests.py:
+ * Scripts/webkitpy/style_references.py:
+
+2010-02-28 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Shinichiro Hamaji.
+
+ Exempted WebKitTools/Scripts/webkitpy/thirdparty from all
+ style checks except for the whitespace/carriage_return check
+ and the pep8 tab and trailing white space checks.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35497
+
+ * Scripts/webkitpy/style/checker.py:
+ - Adjusted the _PATH_RULES_SPECIFIER configuration as necessary.
+ - Added enough pep8 categories to _all_categories() for the
+ unit tests to pass.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated the test_path_rules_specifier() unit test.
+
+2010-02-27 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by David Levin.
+
+ Added Python style checking to check-webkit-style using
+ the third-party pep8 module (via autoinstall).
+
+ https://bugs.webkit.org/show_bug.cgi?id=33639
+
+ * Scripts/webkitpy/style/checker.py:
+ - Added PYTHON to FileType.
+ - Updated ProcessorDispatcher to return a PythonProcessor
+ for *.py files.
+
+ * Scripts/webkitpy/style/checker_unittest.py:
+ - Updated the ProcessorDispatcher unit tests for *.py files.
+
+ * Scripts/webkitpy/style/processors/python.py: Added.
+ - Added PythonProcessor class.
+
+ * Scripts/webkitpy/style/processors/python_unittest.py: Added.
+ - Added PythonProcessor unit tests.
+
+ * Scripts/webkitpy/style/processors/python_unittest_input.py: Added.
+ - Added a sample Python file to test the PythonProcessor.process()
+ code path (since pep8 accepts a file path).
+
+ * Scripts/webkitpy/style/unittests.py:
+ - Updated the style unit test file to import python_unittest.py.
+
+ * Scripts/webkitpy/style_references.py:
+ - Adjusted style references to import pep8.
+
+2010-02-26 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by David Levin.
+
+ Rewrote autoinstall.py to support unzipping *.zip files after
+ download, unzipping and extracting *.tar.gz files after download,
+ and copying installed files to a destination directory.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35163
+
+ These changes will allow us to autoinstall pep8.py from the web
+ and to put our third-party autoinstalled code in an explicit
+ directory like webkitpy/thirdparty/autoinstalled. They should
+ also speed up imports from autoinstalled *.zip packages slightly
+ since *.pyc files cannot be generated when importing from
+ zipped packages.
+
+ * Scripts/webkitpy/__init__.py:
+ - Updated the autoinstall lines to use the new autoinstall methods.
+ - Added pep8.py to the list of auto-installed packages.
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Updated mechanize import path.
+
+ * Scripts/webkitpy/init/__init__.py: Copied from WebKitTools/QueueStatusServer/filters/__init__.py.
+
+ * Scripts/webkitpy/init/autoinstall.py: Added.
+ - Added AutoInstaller class.
+ - Added sample testing usage to __main__.
+
+ * Scripts/webkitpy/networktransaction.py:
+ - Updated mechanize import path.
+
+ * Scripts/webkitpy/networktransaction_unittest.py:
+ - Updated mechanize import path.
+
+ * Scripts/webkitpy/statusserver.py:
+ - Updated mechanize import path.
+
+ * Scripts/webkitpy/thirdparty/autoinstall.py: Removed.
+ - Replaced with rewritten autoinstall in init/autoinstall.py.
+
+2010-02-26 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Change the default port for new-run-webkit-tests when running on
+ a Mac from 'chromium-mac' to 'mac'. Add a '--chromium' switch to
+ pick up the default platform-specific version of chromium instead.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35462
+
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
+2010-02-26 Dirk Pranke <dpranke@chromium.org>
+
+ Unreviewed, build fix
+
+ third time's the charm getting this path right?
+
+ * pywebsocket/mod_pywebsocket/standalone.py:
+
+2010-02-26 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, build fix.
+
+ * pywebsocket/mod_pywebsocket/standalone.py:
+
+2010-02-26 Dumitru Daniliuc <dumi@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Moving the script to the right location.
+
+ * pywebsocket/mod_pywebsocket: Added.
+ * pywebsocket/mod_pywebsocket/standalone.py: Copied from WebKitTools/pywebsocket/standalone.py.
+ * pywebsocket/standalone.py: Removed.
+
+2010-02-26 Dumitru Daniliuc <dumi@chromium.org>
+
+ Unreviewed, fixing the license.
+
+ * pywebsocket/standalone.py:
+
+2010-02-26 Dumitru Daniliuc <dumi@chromium.org>
+
+ Reviewed by Adam Barth.
+
+ Adding pywebsocket/standalone.py back to fix the Chromium webkit
+ canaries. Once all layout tests pass, we can get in the patch that
+ moves this directorty to WebKitTools/Scripts, update all Chromium
+ scripts, and revert this patch.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35464
+
+ * pywebsocket: Added.
+ * pywebsocket/standalone.py: Added.
+
+2010-02-26 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk>
+
+ Reviewed by Xan Lopez.
+
+ [GTK] Needs proper reporting of frame loader callbacks, in DRT
+ https://bugs.webkit.org/show_bug.cgi?id=32170
+
+ Fix reporting of unload handlers, so that it is emitted where
+ needed, not after it.
+
+ * DumpRenderTree/gtk/DumpRenderTree.cpp:
+ (webViewLoadFinished):
+ (webViewDocumentLoadFinished):
+ (createWebView):
+
+2010-02-26 Antonio Gomes <tonikitoo@webkit.org>
+
+ Reviewed by Gustavo Noronha.
+ Patch by Antonio Gomes <tonikitoo@webkit.org>
+
+ [Gtk] Make DRT EventSender::keyDown to consider 'hardware_keycode' field when synthesizing an event.
+ https://bugs.webkit.org/show_bug.cgi?id=35432
+
+ When a directional key-press event (arrow keys, End, Home, PageUp,
+ PageDown, etc) is synthesized by DRT's EventSender and it targets
+ an editor (e.g. <input type=text>, <textare>, etc), event is processed
+ by WebCore::EventHandler. On the other hand, if event target is not
+ an editor, event is bubbled up to Gtk+ for processing. In such cases,
+ if 'hardware_keycode' is not provided at event synthesize time
+ its processing fails (at some point in gtk_bindings_activate_event),
+ and no scroll action is performed.
+
+ Unskip fast/events/node-event-anchor-lock.html
+
+ * DumpRenderTree/win/EventSender.cpp:
+ (keyDownCallback):
+ * platform/gtk/Skipped:
+
+2010-02-26 Dimitri Glazkov <dglazkov@chromium.org>
+
+ Reviewed by David Levin.
+
+ new-webkit-run-tests: Extraneous parenthesis in websocket_server.py
+ https://bugs.webkit.org/show_bug.cgi?id=35436
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py: Removed extra paren.
+
+2010-02-26 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by David Levin.
+
+ Moved pywebsocket into the webkitpy/thirdparty directory and added
+ an associated README.webkit file to the pywebsocket directory.
+ This makes pywebsocket more consistent with the other third-party
+ Python code in our repository.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35198
+
+ * Scripts/run-webkit-tests:
+ - Updated paths to pywebsocket.
+
+ * Scripts/run-webkit-websocketserver:
+ - Updated paths to pywebsocket.
+
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+ - Updated paths to pywebsocket.
+
+ * Scripts/webkitpy/thirdparty/pywebsocket: Copied from WebKitTools/pywebsocket.
+ * Scripts/webkitpy/thirdparty/pywebsocket/README.webkit: Added.
+ - Added a file containing information about the contents
+ of the pywebsocket directory. This will make it easier to
+ understand where the third-party code came from and what
+ local changes have been made.
+
+ * pywebsocket: Removed.
+ * pywebsocket/COPYING: Removed.
+ * pywebsocket/MANIFEST.in: Removed.
+ * pywebsocket/README: Removed.
+ * pywebsocket/example: Removed.
+ * pywebsocket/example/echo_client.py: Removed.
+ * pywebsocket/example/echo_wsh.py: Removed.
+ * pywebsocket/mod_pywebsocket: Removed.
+ * pywebsocket/mod_pywebsocket/__init__.py: Removed.
+ * pywebsocket/mod_pywebsocket/dispatch.py: Removed.
+ * pywebsocket/mod_pywebsocket/handshake.py: Removed.
+ * pywebsocket/mod_pywebsocket/headerparserhandler.py: Removed.
+ * pywebsocket/mod_pywebsocket/memorizingfile.py: Removed.
+ * pywebsocket/mod_pywebsocket/msgutil.py: Removed.
+ * pywebsocket/mod_pywebsocket/standalone.py: Removed.
+ * pywebsocket/mod_pywebsocket/util.py: Removed.
+ * pywebsocket/setup.py: Removed.
+ * pywebsocket/test: Removed.
+ * pywebsocket/test/config.py: Removed.
+ * pywebsocket/test/mock.py: Removed.
+ * pywebsocket/test/run_all.py: Removed.
+ * pywebsocket/test/test_dispatch.py: Removed.
+ * pywebsocket/test/test_handshake.py: Removed.
+ * pywebsocket/test/test_memorizingfile.py: Removed.
+ * pywebsocket/test/test_mock.py: Removed.
+ * pywebsocket/test/test_msgutil.py: Removed.
+ * pywebsocket/test/test_util.py: Removed.
+ * pywebsocket/test/testdata: Removed.
+ * pywebsocket/test/testdata/handlers: Removed.
+ * pywebsocket/test/testdata/handlers/blank_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/origin_check_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub: Removed.
+ * pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub/plain_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py: Removed.
+ * pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py: Removed.
+
+2010-02-26 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Changed the diff_parser module to log to a module-specific
+ logging.logger rather than the root logger.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35391
+
+ * Scripts/webkitpy/diff_parser.py:
+
+2010-02-26 Csaba Osztrogonác <ossy@webkit.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] LayoutTestController.numberOfPages() should have default parameters
+ https://bugs.webkit.org/show_bug.cgi?id=35428
+
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp: maxViewWidth and maxViewHeight moved to
+ LayoutTestController to converge to platform independent implementation.
+ (WebCore::DumpRenderTree::DumpRenderTree):
+ (WebCore::DumpRenderTree::open):
+ * DumpRenderTree/qt/LayoutTestControllerQt.cpp: Initialization of maxViewWidth and maxViewHeight added.
+ * DumpRenderTree/qt/LayoutTestControllerQt.h:
+ - Default parameters for LayoutTestController.numberOfPages() added.
+ - maxViewWidth and maxViewHeight members added.
+
+2010-02-26 Jamey Hicks <jamey.hicks@nokia.com>
+
+ Reviewed by Laszlo Gombos.
+
+ [Qt] added QWebSettings::setInspectorUrl() and QWebSettings::inspectorUrl()
+
+ Enables the use of alternate Web Inspector frontends by changing
+ the location of the frontend. The location is specified by providing
+ -inspector-url url
+ as an argument to QtLauncher.
+
+ This is required so that the Web Inspector may be run from an
+ external process or an external tool such as Eclipse or Aptana may
+ be used instead of the in-process Web Inspector UI.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35340
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::init):
+ (LauncherApplication::handleUserOptions):
+
+2010-02-25 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Add a 'passing' port implementation to new-run-webkit-tests that
+ acts as a wrapper around an existing implementation but stubs out
+ the actual test invocations (instead, the expected results are echoed
+ back to the harness). This is useful for coverage and perf testing
+ of the harness (especially perf testing as it essentially provides
+ a lower bound on how fast the harness can run).
+
+ Also added a --nostart-helper flag to new-run-webkit-tests so that
+ you can skip starting the layout_test_helper and actually run the
+ harness even if you don't have a build of that port.
+
+ Also fix a bug in the 'test' port implementation to actually
+ create the results directory under /tmp instead of /.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35370
+
+ * Scripts/webkitpy/layout_tests/port/factory.py: Modified.
+ * Scripts/webkitpy/layout_tests/port/passing.py: Added.
+ * Scripts/webkitpy/layout_tests/port/test.py: Added.
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py: Modified.
+
+2010-02-25 Eric Seidel <eric@webkit.org>
+
+ Fix typo in my last change. No review.
+
+ Rename run-chromium-webkit-tests to new-run-webkit-tests to stop confusion
+ https://bugs.webkit.org/show_bug.cgi?id=35408
+
+ * Scripts/new-run-webkit-tests:
+
+2010-02-25 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Rename run-chromium-webkit-tests to new-run-webkit-tests to stop confusion
+ https://bugs.webkit.org/show_bug.cgi?id=35408
+
+ * Scripts/new-run-webkit-tests: Added.
+ * Scripts/run-chromium-webkit-tests: Removed.
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py: Renamed from WebKitTools/Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py.
+
+2010-02-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by David Levin.
+
+ EWS leaks memory slowly
+ https://bugs.webkit.org/show_bug.cgi?id=35395
+
+ The EWS bots leak memory very slowly. If you run them for about a
+ month, each one will take up around 1 GB of virutal memory. If you run
+ several of them on one machine, you'll eventually exhaust all available
+ memory and grind the bots to a halt.
+
+ This patch introduces a --exit-after-iteration option to the queues so
+ that we run them for a finite amount of time. Once they exit and
+ restart, they'll reclaim the leaked memory. I'm not sure how many
+ iterations I'll end up running them for. I'll need to sort that out
+ operationally, but my initial guess is around 1000.
+
+ * Scripts/webkitpy/commands/queues.py:
+ * Scripts/webkitpy/commands/queues_unittest.py:
+
+2010-02-25 Jarkko Sakkinen <jarkko.sakkinen@tieto.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Qt WebGL support
+
+ Adds toggling of WebGL support to QtLauncher.
+ https://bugs.webkit.org/show_bug.cgi?id=35153
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::toggleWebGL):
+ (LauncherWindow::setupUI):
+
+2010-02-25 Ben Murdoch <benm@google.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ The target element of a Touch should be the target where that touch originated, not where it is now.
+ https://bugs.webkit.org/show_bug.cgi?id=34585
+
+ * DumpRenderTree/qt/EventSenderQt.cpp:
+ (EventSender::addTouchPoint): Fix a bug where touch points were not being given unique ids.
+
+2010-02-24 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] QtLauncher has a border when running on QGraphicsView mode
+ https://bugs.webkit.org/show_bug.cgi?id=35352
+
+ Fix 2-pixels frame on each border of QtLauncher when running on QGraphicsView mode.
+
+ * QtLauncher/webview.cpp:
+ (WebViewGraphicsBased::WebViewGraphicsBased):
+
+2010-02-23 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by David Levin.
+
+ When the run-chromium-webkit-tests code was landed and the code was
+ refactored into the 'port' package, I accidentally broke using
+ http_server.py or websocket_server.py as command-line scripts
+ (the constructors needed a port object they weren't getting). This
+ change fixes them so that --server start|stop actually works.
+
+ As a part of this, the two files need to be able to call port.get(),
+ but doing that is awkward from a file inside the package, so I moved
+ get() into factory.py and imported that into __init__.py so that
+ http_server.py and websocket_server.py can just import factory.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35316
+
+ * Scripts/webkitpy/layout_tests/port/__init__.py:
+ * Scripts/webkitpy/layout_tests/port/factory.py:
+ * Scripts/webkitpy/layout_tests/port/http_server.py:
+ * Scripts/webkitpy/layout_tests/port/websocket_server.py:
+
+2010-02-24 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by David Levin.
+
+ Fix the function signature for check_sys_deps on the mac port, and
+ fix the ordering of port_obj.check_sys_deps() and
+ port_obj.start_helper() (helper needs to be started before we check
+ the system configuration).
+
+ http://bugs.webkit.org/show_bug.cgi?id=35367
+
+ * Scripts/webkitpy/layout_tests/port/mac.py:
+ * Scripts/webkitpy/layout_tests/port/test.py:
+ * Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py:
+
+2010-02-24 James Robinson <jamesr@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ Provide default username parameter to SVN.commit_with_message().
+
+ * Scripts/webkitpy/scm.py:
+
+2010-02-24 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Siedel.
+
+ Check the proper directory for a WebKit SVN version (Chromium does
+ not check out the entire WebKit tree directly, but rather pulls
+ individual subdirectories. So, checking for the SVN version in
+ WebKit/WebCore rather than just in WebKit works more reliably across
+ different ports).
+
+ http://bugs.webkit.org/show_bug.cgi?id=35321
+
+ * Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py:
+
+2010-02-24 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Dan Bernstein.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35357
+ Two editing tests fail after DumpRenderTree run loop changes
+
+ AppKit decided that it wanted to group all editing commands for some reason (and thus undo
+ reverted them all at once).
+
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (dump): Reverted the change that made DumpRenderTree use -[NSApplication run].
+ (runTest): Ditto.
+ (-[DumpRenderTreeApplication isRunning]): Override isRunning with a function that always
+ returns YES. This is another way to make the Java plug-in work.
+
+2010-02-23 Adam Roben <aroben@apple.com>
+
+ Make commit-log-editor find just-added ChangeLog files
+
+ Fixes <http://webkit.org/b/35294> commit-log-editor doesn't find
+ just-added ChangeLog files
+
+ Reviewed by Dave Levin.
+
+ * Scripts/commit-log-editor:
+ (top level): Modified the regular expression that's used to find
+ modified ChangeLog files to also look for just-added ChangeLog files.
+
+2010-02-24 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by Darin Adler.
+
+ check-webkit-style false positive for WebCore forwarding header
+ https://bugs.webkit.org/show_bug.cgi?id=34604
+
+ * Scripts/webkitpy/style/checker.py:
+ * Scripts/webkitpy/style/checker_unittest.py:
+
+2010-02-23 Alexey Proskuryakov <ap@apple.com>
+
+ Reviewed by Mark Rowe.
+
+ https://bugs.webkit.org/show_bug.cgi?id=22602
+ Enable Java in DumpRenderTree (on Mac)
+
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (exitApplicationRunLoop):
+ (dump):
+ (runTest):
+ DumpRenderTree now runs an NSApplication, not just an event loop. This way, the Java plug-in
+ can start without freezing.
+
+ * Scripts/run-webkit-tests: Compile java sources in LayputTests/java.
+
+2010-02-23 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Need a final integration between QtLauncher and QGVLauncher
+ https://bugs.webkit.org/show_bug.cgi?id=35292
+
+ WebKit coding style fixes.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::resetZoom):
+
+2010-02-23 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Need a final integration between QtLauncher and QGVLauncher
+ https://bugs.webkit.org/show_bug.cgi?id=35292
+
+ Add cloneWindow feature to QtLauncher, when running on QGraphicsView mode.
+
+ * QtLauncher/main.cpp:
+ (LauncherWindow::LauncherWindow):
+ (LauncherWindow::~LauncherWindow):
+ (LauncherWindow::init):
+ (LauncherWindow::cloneWindow):
+ (LauncherWindow::setupUI):
+
+2010-02-23 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Dimitri Glazkov.
+
+ Fix handling of check_wdiff_install when wdiff isn't installed.
+
+ http://bugs.webkit.org/show_bug.cgi?id=35304
+
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+
+2010-02-23 Leandro Pereira <leandro@profusion.mobi>
+
+ Reviewed by NOBODY.
+
+ Fix false positives for 'delete *pointer' statements.
+ http://webkit.org/b/35235
+
+ * WebKitTools/Scripts/webkitpy/style/processors/cpp.py:
+
+2010-02-23 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by Eric Seidel.
+
+ [Gtk] Implement layoutTestController.numberOfPages
+ https://bugs.webkit.org/show_bug.cgi?id=35228
+
+ * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp:
+ (LayoutTestController::numberOfPages):
+
+2010-02-23 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] QtLauncher should not use internal JavaScriptCore and WebCore interfaces
+ https://bugs.webkit.org/show_bug.cgi?id=35248
+
+ * QtLauncher/QtLauncher.pro:
+ * QtLauncher/utils.h:
+
+2010-02-23 Daniel Bates <dbates@rim.com>
+
+ Reviewed by Eric Seidel.
+
+ https://bugs.webkit.org/show_bug.cgi?id=34439
+
+ Prompts a person for their SVN username if not already cached (by Subversion).
+
+ Currently, webkit-patch is unable to commit to the SVN repo. unless the
+ WebKit SVN username is already cached (from of a prior commit by hand)
+ because "svn commit" (called by webkit-patch) defaults to using the system
+ login name unless the username is already cached or specified on the
+ command line.
+
+ * Scripts/webkitpy/scm.py: Added methods SVN.has_authorization_for_realm and
+ modified SVN.commit_with_message to call it. Added optional username parameter
+ to method SVN.commit_with_message.
+ * Scripts/webkitpy/scm_unittest.py: Added unit test methods: SVNTest.test_commit_with_username,
+ SVNTest.test_has_authorization_for_realm, and SVNTest.test_not_have_authorization_for_realm.
+
+2010-02-22 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Eric Siedel.
+
+ Add more checking for missing binaries and packages to check_sys_deps()
+
+ https://bugs.webkit.org/show_bug.cgi?id=35062
+
+ * Scripts/webkitpy/layout_tests/port/base.py:
+ * Scripts/webkitpy/layout_tests/port/chromium.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_linux.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_mac.py:
+ * Scripts/webkitpy/layout_tests/port/chromium_win.py:
+ * Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py:
+
+2010-02-22 Shinichiro Hamaji <hamaji@chromium.org>
+
+ Reviewed by David Levin.
+
+ [Gtk] check-webkit-style: GTK style should be allowed in WebKitTools/DumpRenderTree/gtk
+ https://bugs.webkit.org/show_bug.cgi?id=35229
+
+ * Scripts/webkitpy/style/checker.py:
+ * Scripts/webkitpy/style/checker_unittest.py:
+
+2010-02-22 James Robinson <jamesr@chromium.org>
+
+ Unreviewed. Adding myself to committers list.
+
+ * Scripts/webkitpy/committers.py:
+
+2010-02-22 Simon Fraser <simon.fraser@apple.com>
+
+ Reviewed by Darin Adler.
+
+ Remove DRT hack that turns off hardware acceleration with older QuickTimes
+ https://bugs.webkit.org/show_bug.cgi?id=35275
+
+ Now that WebKit does a version check to avoid a QuickTime-related
+ crash (r55100), DumpRenderTree does not need to.
+
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetDefaultsToConsistentValues):
+
+2010-02-22 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Build the DRT in debug on Mac OS X
+
+ * DumpRenderTree/qt/DumpRenderTree.pro:
+
+2010-02-22 Laszlo Gombos <laszlo.1.gombos@nokia.com>
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ [Qt] Add support for layout tests on Symbian
+ https://bugs.webkit.org/show_bug.cgi?id=31589
+
+ * DumpRenderTree/qt/DumpRenderTree.pro:
+ * DumpRenderTree/qt/DumpRenderTreeQt.cpp:
+ * DumpRenderTree/qt/main.cpp:
+ (main):
+
+2010-02-20 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved simplejson into webkitpy/thirdparty directory.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35196
+
+ * Scripts/run-chromium-webkit-tests:
+ - Added webkitpy/thirdparty to the script's search path.
+
+ * Scripts/webkitpy/thirdparty/simplejson: Copied from WebKitTools/simplejson.
+ - Copied simplejson directory.
+
+ * simplejson: Removed.
+ * simplejson/LICENSE.txt: Removed.
+ * simplejson/README.txt: Removed.
+ * simplejson/__init__.py: Removed.
+ * simplejson/_speedups.c: Removed.
+ * simplejson/decoder.py: Removed.
+ * simplejson/encoder.py: Removed.
+ * simplejson/jsonfilter.py: Removed.
+ * simplejson/scanner.py: Removed.
+
+2010-02-20 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Moved BeautifulSoup to webkitpy/thirdparty directory.
+
+ https://bugs.webkit.org/show_bug.cgi?id=35195
+
+ * Scripts/webkitpy/BeautifulSoup.py: Removed.
+ - Moved to webkitpy/thirdparty.
+
+ * Scripts/webkitpy/bugzilla.py:
+ - Updated import statement.
+
+ * Scripts/webkitpy/bugzilla_unittest.py:
+ - Updated import statement.
+
+ * Scripts/webkitpy/buildbot.py:
+ - Updated import statement.
+
+ * Scripts/webkitpy/buildbot_unittest.py:
+ - Updated import statement.
+
+ * Scripts/webkitpy/statusserver.py:
+ - Updated import statement.
+
+ * Scripts/webkitpy/thirdparty/BeautifulSoup.py: Copied from WebKitTools/Scripts/webkitpy/BeautifulSoup.py.
+
+2010-02-20 Chris Jerdonek <cjerdonek@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Created a directory for third-party Python code, and moved
+ autoinstall.py into it.
+
+ https://bugs.webkit.org/show_bug.cgi?id=34540
+
+ * Scripts/webkitpy/__init__.py:
+ - Updated "import autoinstall" statement.
+
+ * Scripts/webkitpy/autoinstall.py: Removed.
+ - Moved to thirdparty/autoinstall.py.
+
+ * Scripts/webkitpy/thirdparty: Added.
+ * Scripts/webkitpy/thirdparty/__init__.py: Added.
+ * Scripts/webkitpy/thirdparty/autoinstall.py: Copied from WebKitTools/Scripts/webkitpy/autoinstall.py.
+
+2010-02-20 Gustavo Noronha Silva <gns@gnome.org>
+
+ Unreviewed, obvious fix for the python failure in our new buildbot
+ step:
+
+ http://build.webkit.org/builders/GTK Linux 32-bit Release/builds/9075/steps/API tests/logs/err.text
+
+ * BuildSlaveSupport/build.webkit.org-config/master.cfg:
+
+2010-02-19 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by David Levin.
+
+ Add an ENABLE flag for sandboxed iframes to make it possible to disable it in releases
+ https://bugs.webkit.org/show_bug.cgi?id=35147
+
+ * Scripts/build-webkit: Handle new flag.
+
+2010-02-19 Leandro Pereira <leandro@profusion.mobi>
+
+ Reviewed by Shinichiro Hamaji.
+
+ [style-queue] should not complain about identifier names with underscores under WebKit/efl/ewk/
+ https://bugs.webkit.org/show_bug.cgi?id=35091
+
+ White list unix_hacker_style names in WebKit/efl/ewk because these
+ are used in the EFL API.
+
+ * Scripts/webkitpy/style/checker.py:
+ - Filter out readability/naming on WebKit/efl/ewk.
+
+2010-02-19 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Adam Barth.
+
+ Split out "prepare-rollout" from "rollout" and make --complete-rollout default
+ https://bugs.webkit.org/show_bug.cgi?id=33745
+
+ * Scripts/webkitpy/commands/download.py:
+ - Add a new AbstractRolloutPrepCommand to share code between PrepareRollout and Rollout
+ - Add PrepareRollout
+ * Scripts/webkitpy/commands/download_unittest.py: Test PrepareRollout, remove CompleteRollout tests.
+ * Scripts/webkitpy/steps/__init__.py: include ReopenBugAfterRollout step.
+ * Scripts/webkitpy/steps/completerollout.py: Removed.
+ * Scripts/webkitpy/steps/options.py: remove complete_rollout
+ * Scripts/webkitpy/steps/reopenbugafterrollout.py: Added.
+
2010-02-19 Jesus Sanchez-Palencia <jesus.palencia@openbossa.org>
Reviewed by Kenneth Rohde Christiansen.
diff --git a/WebKitTools/DumpRenderTree/AccessibilityController.cpp b/WebKitTools/DumpRenderTree/AccessibilityController.cpp
index 045bc80..798389f 100644
--- a/WebKitTools/DumpRenderTree/AccessibilityController.cpp
+++ b/WebKitTools/DumpRenderTree/AccessibilityController.cpp
@@ -77,12 +77,26 @@ static JSValueRef logScrollingStartEventsCallback(JSContextRef ctx, JSObjectRef,
return JSValueMakeUndefined(ctx);
}
+static JSValueRef getElementAtPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int x = 0;
+ int y = 0;
+ if (argumentCount == 2) {
+ x = JSValueToNumber(context, arguments[0], exception);
+ y = JSValueToNumber(context, arguments[1], exception);
+ }
+
+ AccessibilityController* controller = static_cast<AccessibilityController*>(JSObjectGetPrivate(thisObject));
+ return AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->elementAtPoint(x, y));
+}
+
JSClassRef AccessibilityController::getJSClass()
{
static JSStaticFunction staticFunctions[] = {
{ "logFocusEvents", logFocusEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "logValueChangeEvents", logValueChangeEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "logScrollingStartEvents", logScrollingStartEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "elementAtPoint", getElementAtPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ 0, 0, 0 }
};
diff --git a/WebKitTools/DumpRenderTree/AccessibilityController.h b/WebKitTools/DumpRenderTree/AccessibilityController.h
index de58f84..5a6ca13 100644
--- a/WebKitTools/DumpRenderTree/AccessibilityController.h
+++ b/WebKitTools/DumpRenderTree/AccessibilityController.h
@@ -45,6 +45,7 @@ public:
// Controller Methods - platform-independent implementations
AccessibilityUIElement rootElement();
AccessibilityUIElement focusedElement();
+ AccessibilityUIElement elementAtPoint(int x, int y);
void setLogFocusEvents(bool);
void setLogValueChangeEvents(bool);
diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp
index 87fe05c..9cf34de 100644
--- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp
+++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp
@@ -344,6 +344,12 @@ static JSValueRef showMenuCallback(JSContextRef context, JSObjectRef function, J
return JSValueMakeUndefined(context);
}
+static JSValueRef pressCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->press();
+ return JSValueMakeUndefined(context);
+}
+
static JSValueRef takeFocusCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
toAXElement(thisObject)->takeFocus();
@@ -435,6 +441,12 @@ static JSValueRef getLanguageCallback(JSContextRef context, JSObjectRef thisObje
return JSValueMakeString(context, language.get());
}
+static JSValueRef getHelpTextCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ JSRetainPtr<JSStringRef> language(Adopt, toAXElement(thisObject)->helpText());
+ return JSValueMakeString(context, language.get());
+}
+
static JSValueRef getOrientationCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
{
JSRetainPtr<JSStringRef> orientation(Adopt, toAXElement(thisObject)->orientation());
@@ -446,6 +458,16 @@ static JSValueRef getChildrenCountCallback(JSContextRef context, JSObjectRef thi
return JSValueMakeNumber(context, toAXElement(thisObject)->childrenCount());
}
+static JSValueRef rowCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->rowCount());
+}
+
+static JSValueRef columnCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
+{
+ return JSValueMakeNumber(context, toAXElement(thisObject)->columnCount());
+}
+
static JSValueRef getXCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
{
return JSValueMakeNumber(context, toAXElement(thisObject)->x());
@@ -602,6 +624,12 @@ static JSValueRef addNotificationListenerCallback(JSContextRef context, JSObject
return JSValueMakeBoolean(context, succeeded);
}
+static JSValueRef removeNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ toAXElement(thisObject)->removeNotificationListener();
+ return JSValueMakeUndefined(context);
+}
+
// Destruction
static void finalize(JSObjectRef thisObject)
@@ -626,6 +654,7 @@ JSClassRef AccessibilityUIElement::getJSClass()
{ "title", getTitleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "description", getDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "language", getLanguageCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "helpText", getHelpTextCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "stringValue", getStringValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "x", getXCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "y", getYCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
@@ -637,6 +666,8 @@ JSClassRef AccessibilityUIElement::getJSClass()
{ "minValue", getMinValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "maxValue", getMaxValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "childrenCount", getChildrenCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "rowCount", rowCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "columnCount", columnCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "insertionPointLineNumber", getInsertionPointLineNumberCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "selectedTextRange", getSelectedTextRangeCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "isEnabled", getIsEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
@@ -696,12 +727,14 @@ JSClassRef AccessibilityUIElement::getJSClass()
{ "increment", incrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "decrement", decrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "showMenu", showMenuCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "press", pressCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "disclosedRowAtIndex", disclosedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "ariaOwnsElementAtIndex", ariaOwnsElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "ariaFlowToElementAtIndex", ariaFlowToElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "selectedRowAtIndex", selectedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "isEqual", isEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "addNotificationListener", addNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeNotificationListener", removeNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "takeFocus", takeFocusCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "takeSelection", takeSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "addSelection", addSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h
index e7d3bc7..f62ec1a 100644
--- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h
+++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h
@@ -51,6 +51,14 @@ typedef AtkObject* PlatformUIElement;
typedef void* PlatformUIElement;
#endif
+#if PLATFORM(MAC)
+#ifdef __OBJC__
+typedef id NotificationHandler;
+#else
+typedef struct objc_object* NotificationHandler;
+#endif
+#endif
+
class AccessibilityUIElement {
public:
AccessibilityUIElement(PlatformUIElement);
@@ -89,6 +97,7 @@ public:
void increment();
void decrement();
void showMenu();
+ void press();
// Attributes - platform-independent implementations
JSStringRef stringAttributeValue(JSStringRef attribute);
@@ -104,6 +113,7 @@ public:
JSStringRef language();
JSStringRef stringValue();
JSStringRef accessibilityValue() const;
+ JSStringRef helpText() const;
JSStringRef orientation() const;
double x();
double y();
@@ -143,6 +153,8 @@ public:
int indexInTable();
JSStringRef rowIndexRange();
JSStringRef columnIndexRange();
+ int rowCount();
+ int columnCount();
// Tree/Outline specific attributes
AccessibilityUIElement selectedRowAtIndex(unsigned);
@@ -170,12 +182,17 @@ public:
// Notifications
// Function callback should take one argument, the name of the notification.
bool addNotificationListener(JSObjectRef functionCallback);
+ // Make sure you call remove, because you can't rely on objects being deallocated in a timely fashion.
+ void removeNotificationListener();
private:
static JSClassRef getJSClass();
-
PlatformUIElement m_element;
- JSObjectRef m_notificationFunctionCallback;
+
+ // A retained, platform specific object used to help manage notifications for this object.
+#if PLATFORM(MAC)
+ NotificationHandler m_notificationHandler;
+#endif
};
#endif // AccessibilityUIElement_h
diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp b/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp
new file mode 100644
index 0000000..82b8671
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp
@@ -0,0 +1,186 @@
+#
+# Copyright (C) 2010 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.
+#
+
+{
+ 'includes': [
+ '../../../WebKit/chromium/features.gypi',
+ ],
+ 'variables': {
+ 'webkit_top': '../../..',
+ 'webkit_api_dir': '<(webkit_top)/WebKit/chromium',
+ 'conditions': [
+ # Location of the chromium src directory and target type is different
+ # if webkit is built inside chromium or as standalone project.
+ ['inside_chromium_build==0', {
+ # DumpRenderTree is being built outside of the full chromium project.
+ # e.g. via build-dumprendertree --chromium
+ 'chromium_src_dir': '<(webkit_api_dir)',
+ 'webkit_support_gyp': '<(webkit_api_dir)/webkit/support/webkit_support.gyp',
+ },{
+ # WebKit is checked out in src/chromium/third_party/WebKit
+ 'chromium_src_dir': '<(webkit_top)/../..',
+ 'webkit_support_gyp': '<(webkit_top)/../../webkit/webkit.gyp',
+ }],
+ ],
+ },
+ 'target_defaults': {
+ 'target_conditions': [
+ ['OS!="linux" and OS!="freebsd" and OS!="openbsd"', {
+ 'sources/': [
+ ['exclude', '(Gtk|Linux)\\.cpp$']
+ ]
+ }],
+ ['OS!="win"', {
+ 'sources/': [
+ ['exclude', 'Win\\.cpp$'],
+ ]
+ }],
+ ['OS!="mac"', {
+ 'sources/': [
+ # .mm is already excluded by common.gypi
+ ['exclude', 'Mac\\.cpp$'],
+ ]
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'DumpRenderTree',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'dependencies': [
+ '<(webkit_api_dir)/WebKit.gyp:webkit',
+ '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf_config',
+ '<(chromium_src_dir)/third_party/icu/icu.gyp:icuuc',
+ '<(chromium_src_dir)/third_party/npapi/npapi.gyp:npapi',
+ '<(chromium_src_dir)/skia/skia.gyp:skia',
+ '<(webkit_support_gyp):webkit_support',
+ ],
+ 'include_dirs': [
+ '.',
+ '<(webkit_api_dir)',
+ '<(webkit_top)/JavaScriptCore',
+ '<(webkit_top)/WebKit/mac/WebCoreSupport', # For WebSystemInterface.h
+ '<(chromium_src_dir)',
+ ],
+ 'defines': [
+ # Technically not a unit test but require functions available only to
+ # unit tests.
+ 'UNIT_TEST',
+ ],
+ 'sources': [
+ '../chromium/AccessibilityController.cpp',
+ '../chromium/AccessibilityController.h',
+ '../chromium/AccessibilityUIElement.cpp',
+ '../chromium/AccessibilityUIElement.h',
+ '../chromium/CppBoundClass.cpp',
+ '../chromium/CppBoundClass.h',
+ '../chromium/CppVariant.cpp',
+ '../chromium/CppVariant.h',
+ '../chromium/DumpRenderTree.cpp',
+ '../chromium/EventSender.cpp',
+ '../chromium/EventSender.h',
+ '../chromium/LayoutTestController.cpp',
+ '../chromium/LayoutTestController.h',
+ '../chromium/MockSpellCheck.cpp',
+ '../chromium/MockSpellCheck.h',
+ '../chromium/PlainTextController.cpp',
+ '../chromium/PlainTextController.h',
+ '../chromium/TestNavigationController.cpp',
+ '../chromium/TestNavigationController.h',
+ '../chromium/TestShell.cpp',
+ '../chromium/TestShell.h',
+ '../chromium/TestShellGtk.cpp',
+ '../chromium/TestShellMac.mm',
+ '../chromium/TestShellWin.cpp',
+ '../chromium/TextInputController.cpp',
+ '../chromium/TextInputController.h',
+ '../chromium/WebViewHost.cpp',
+ '../chromium/WebViewHost.h',
+ ],
+ 'mac_bundle_resources': [
+ '../qt/fonts/AHEM____.TTF',
+ '../fonts/WebKitWeightWatcher100.ttf',
+ '../fonts/WebKitWeightWatcher200.ttf',
+ '../fonts/WebKitWeightWatcher300.ttf',
+ '../fonts/WebKitWeightWatcher400.ttf',
+ '../fonts/WebKitWeightWatcher500.ttf',
+ '../fonts/WebKitWeightWatcher600.ttf',
+ '../fonts/WebKitWeightWatcher700.ttf',
+ '../fonts/WebKitWeightWatcher800.ttf',
+ '../fonts/WebKitWeightWatcher900.ttf',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'dependencies': ['LayoutTestHelper'],
+ }],
+ ],
+ },
+
+ {
+ 'target_name': 'ImageDiff',
+ 'type': 'executable',
+ 'dependencies': [
+ '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf',
+ '<(chromium_src_dir)/gfx/gfx.gyp:gfx',
+ ],
+ 'include_dirs': [
+ '<(webkit_top)/JavaScriptCore',
+ '<(chromium_src_dir)',
+ ],
+ 'sources': [
+ '../chromium/ImageDiff.cpp',
+ ],
+ },
+ ], # targets
+
+ 'conditions': [
+ ['OS=="mac"', {
+ 'targets': [
+ {
+ 'target_name': 'LayoutTestHelper',
+ 'type': 'executable',
+ 'sources': ['../chromium/LayoutTestHelper.mm'],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ ],
+ },
+ },
+ ],
+ }],
+ ], # conditions
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj b/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
index 06f0599..3adfaf2 100644
--- a/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
+++ b/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
@@ -35,6 +35,8 @@
1AC6C84A0D07638600CD3161 /* PluginObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7800D07589B00CD3161 /* PluginObject.cpp */; };
1AC6C84B0D07638600CD3161 /* TestObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7810D07589B00CD3161 /* TestObject.cpp */; };
23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23BCB88F0EA57623003C6289 /* OpenGL.framework */; };
+ 3713EDE2115BE19300705720 /* ColorBits-A.png in Copy Font Files */ = {isa = PBXBuildFile; fileRef = 3713EDDF115BE16F00705720 /* ColorBits-A.png */; };
+ 3713EDE3115BE19300705720 /* ColorBits.ttf in Copy Font Files */ = {isa = PBXBuildFile; fileRef = 3713EDE0115BE16F00705720 /* ColorBits.ttf */; };
5185F6B210714E07007AA393 /* HistoryDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5185F69F10714A57007AA393 /* HistoryDelegate.mm */; };
5185F6B310714E12007AA393 /* HistoryDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5185F69E10714A57007AA393 /* HistoryDelegate.h */; };
5DB9AC970F722C3600684641 /* AHEM____.TTF in Copy Font Files */ = {isa = PBXBuildFile; fileRef = AA7F10C20CB3C1030003BDC9 /* AHEM____.TTF */; };
@@ -156,6 +158,8 @@
dstSubfolderSpec = 7;
files = (
5DB9AC970F722C3600684641 /* AHEM____.TTF in Copy Font Files */,
+ 3713EDE2115BE19300705720 /* ColorBits-A.png in Copy Font Files */,
+ 3713EDE3115BE19300705720 /* ColorBits.ttf in Copy Font Files */,
5DB9AC980F722C3600684641 /* WebKitWeightWatcher100.ttf in Copy Font Files */,
5DB9AC990F722C3600684641 /* WebKitWeightWatcher200.ttf in Copy Font Files */,
5DB9AC9A0F722C3600684641 /* WebKitWeightWatcher300.ttf in Copy Font Files */,
@@ -182,6 +186,8 @@
1AC6C7810D07589B00CD3161 /* TestObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestObject.cpp; sourceTree = "<group>"; };
23BCB88F0EA57623003C6289 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpRenderTreePrefix.h; sourceTree = "<group>"; };
+ 3713EDDF115BE16F00705720 /* ColorBits-A.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ColorBits-A.png"; path = "fonts/ColorBits-A.png"; sourceTree = "<group>"; };
+ 3713EDE0115BE16F00705720 /* ColorBits.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ColorBits.ttf; path = fonts/ColorBits.ttf; sourceTree = "<group>"; };
375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher100.ttf; path = fonts/WebKitWeightWatcher100.ttf; sourceTree = "<group>"; };
375F09720DAC3CB600C8B4E5 /* WebKitWeightWatcher200.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher200.ttf; path = fonts/WebKitWeightWatcher200.ttf; sourceTree = "<group>"; };
375F09730DAC3CB600C8B4E5 /* WebKitWeightWatcher300.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher300.ttf; path = fonts/WebKitWeightWatcher300.ttf; sourceTree = "<group>"; };
@@ -417,6 +423,8 @@
9345229B0BD12B2C0086EDA0 /* Resources */ = {
isa = PBXGroup;
children = (
+ 3713EDDF115BE16F00705720 /* ColorBits-A.png */,
+ 3713EDE0115BE16F00705720 /* ColorBits.ttf */,
AA7F10C20CB3C1030003BDC9 /* AHEM____.TTF */,
375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */,
375F09720DAC3CB600C8B4E5 /* WebKitWeightWatcher200.ttf */,
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
index f528b31..a736160 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
@@ -74,6 +74,7 @@ LayoutTestController::LayoutTestController(const std::string& testPathOrURL, con
, m_globalFlag(false)
, m_isGeolocationPermissionSet(false)
, m_geolocationPermission(false)
+ , m_handlesAuthenticationChallenges(false)
, m_testPathOrURL(testPathOrURL)
, m_expectedPixelHash(expectedPixelHash)
{
@@ -280,6 +281,14 @@ static JSValueRef addDisallowedURLCallback(JSContextRef context, JSObjectRef fun
return JSValueMakeUndefined(context);
}
+static JSValueRef callShouldCloseOnWebViewCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+
+ return JSValueMakeBoolean(context, controller->callShouldCloseOnWebView());
+}
+
static JSValueRef clearAllDatabasesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
// Has mac & windows implementation
@@ -456,6 +465,23 @@ static JSValueRef keepWebHistoryCallback(JSContextRef context, JSObjectRef funct
return JSValueMakeUndefined(context);
}
+static JSValueRef computedStyleIncludingVisitedInfoCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 1)
+ return JSValueMakeUndefined(context);
+
+ // Has mac implementation
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ return controller->computedStyleIncludingVisitedInfo(context, arguments[0]);
+}
+
+static JSValueRef layerTreeAsTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ return JSValueMakeString(context, controller->layerTreeAsText().get());
+}
+
static JSValueRef notifyDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
// Has mac & windows implementation
@@ -914,14 +940,26 @@ static JSValueRef setXSSAuditorEnabledCallback(JSContextRef context, JSObjectRef
return JSValueMakeUndefined(context);
}
-static JSValueRef setFrameSetFlatteningEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef setSpatialNavigationEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->setSpatialNavigationEnabled(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+
+static JSValueRef setFrameFlatteningEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
// Has mac & windows implementation
if (argumentCount < 1)
return JSValueMakeUndefined(context);
LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
- controller->setFrameSetFlatteningEnabled(JSValueToBoolean(context, arguments[0]));
+ controller->setFrameFlatteningEnabled(JSValueToBoolean(context, arguments[0]));
return JSValueMakeUndefined(context);
}
@@ -1011,6 +1049,25 @@ static JSValueRef setUserStyleSheetLocationCallback(JSContextRef context, JSObje
return JSValueMakeUndefined(context);
}
+static JSValueRef setWillSendRequestClearHeaderCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has mac & windows implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> header(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+
+ size_t maxLength = JSStringGetMaximumUTF8CStringSize(header.get());
+ char* headerBuffer = new char[maxLength + 1];
+ JSStringGetUTF8CString(header.get(), headerBuffer, maxLength + 1);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->setWillSendRequestClearHeader(headerBuffer);
+
+ return JSValueMakeUndefined(context);
+}
+
static JSValueRef setWillSendRequestReturnsNullCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
// Has cross-platform implementation
@@ -1217,7 +1274,25 @@ static JSValueRef waitForPolicyDelegateCallback(JSContextRef context, JSObjectRe
return JSValueMakeUndefined(context);
}
-static JSValueRef whiteListAccessFromOriginCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef addOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 4)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> sourceOrigin(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationProtocol(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> destinationHost(Adopt, JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+ bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->addOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef removeOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
if (argumentCount != 4)
return JSValueMakeUndefined(context);
@@ -1231,7 +1306,22 @@ static JSValueRef whiteListAccessFromOriginCallback(JSContextRef context, JSObje
bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]);
LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
- controller->whiteListAccessFromOrigin(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains);
+ controller->removeOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains);
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setScrollbarPolicyCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount != 2)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> orientation(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> policy(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->setScrollbarPolicy(orientation.get(), policy.get());
return JSValueMakeUndefined(context);
}
@@ -1278,6 +1368,51 @@ static JSValueRef apiTestNewWindowDataLoadBaseURLCallback(JSContextRef context,
return JSValueMakeUndefined(context);
}
+static JSValueRef apiTestGoToCurrentBackForwardItemCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->apiTestGoToCurrentBackForwardItem();
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setWebViewEditableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Has Mac implementation
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->setWebViewEditable(JSValueToBoolean(context, arguments[0]));
+
+ return JSValueMakeUndefined(context);
+}
+
+static JSValueRef markerTextForListItemCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+ return JSValueMakeString(context, controller->markerTextForListItem(context, arguments[0]).get());
+}
+
+static JSValueRef authenticateSessionCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // authenticateSession(url, username, password)
+ if (argumentCount != 3)
+ return JSValueMakeUndefined(context);
+
+ JSRetainPtr<JSStringRef> url(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> username(Adopt, JSValueToStringCopy(context, arguments[1], exception));
+ ASSERT(!*exception);
+ JSRetainPtr<JSStringRef> password(Adopt, JSValueToStringCopy(context, arguments[2], exception));
+ ASSERT(!*exception);
+
+ LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+ controller->authenticateSession(url.get(), username.get(), password.get());
+ return JSValueMakeUndefined(context);
+}
+
// Static Values
static JSValueRef getGlobalFlagCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
@@ -1355,10 +1490,13 @@ JSStaticFunction* LayoutTestController::staticFunctions()
{ "addUserScript", addUserScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "addUserStyleSheet", addUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "apiTestNewWindowDataLoadBaseURL", apiTestNewWindowDataLoadBaseURLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "apiTestGoToCurrentBackForwardItem", apiTestGoToCurrentBackForwardItemCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "callShouldCloseOnWebView", callShouldCloseOnWebViewCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "clearAllDatabases", clearAllDatabasesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "clearBackForwardList", clearBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "clearPersistentUserStyleSheet", clearPersistentUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "closeWebInspector", closeWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "computedStyleIncludingVisitedInfo", computedStyleIncludingVisitedInfoCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "decodeHostName", decodeHostNameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "disableImageLoading", disableImageLoadingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "dispatchPendingLoadRequests", dispatchPendingLoadRequestsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
@@ -1387,7 +1525,9 @@ JSStaticFunction* LayoutTestController::staticFunctions()
{ "grantDesktopNotificationPermission", grantDesktopNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "isCommandEnabled", isCommandEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "keepWebHistory", keepWebHistoryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "layerTreeAsText", layerTreeAsTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "numberOfPages", numberOfPagesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "markerTextForListItem", markerTextForListItemCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "notifyDone", notifyDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "numberOfActiveAnimations", numberOfActiveAnimationsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "overridePreference", overridePreferenceCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
@@ -1404,6 +1544,7 @@ JSStaticFunction* LayoutTestController::staticFunctions()
{ "queueNonLoadingScript", queueNonLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "queueReload", queueReloadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "removeAllVisitedLinks", removeAllVisitedLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "removeOriginAccessWhitelistEntry", removeOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAllowUniversalAccessFromFileURLs", setAllowUniversalAccessFromFileURLsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
@@ -1413,45 +1554,50 @@ JSStaticFunction* LayoutTestController::staticFunctions()
{ "setAuthenticationPassword", setAuthenticationPasswordCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAuthenticationUsername", setAuthenticationUsernameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCallCloseOnWebViews", setCallCloseOnWebViewsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCanOpenWindows", setCanOpenWindowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCloseRemainingWindowsWhenComplete", setCloseRemainingWindowsWhenCompleteCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setCustomPolicyDelegate", setCustomPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setDatabaseQuota", setDatabaseQuotaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setDomainRelaxationForbiddenForURLScheme", setDomainRelaxationForbiddenForURLSchemeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setFrameFlatteningEnabled", setFrameFlatteningEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setGeolocationPermission", setGeolocationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setHandlesAuthenticationChallenges", setHandlesAuthenticationChallengesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "setPOSIXLocale", setPOSIXLocaleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setIconDatabaseEnabled", setIconDatabaseEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setJavaScriptProfilingEnabled", setJavaScriptProfilingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setMainFrameIsFirstResponder", setMainFrameIsFirstResponderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setMockGeolocationError", setMockGeolocationErrorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setNewWindowsCopyBackForwardList", setNewWindowsCopyBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setPOSIXLocale", setPOSIXLocaleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setPersistentUserStyleSheetLocation", setPersistentUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setPopupBlockingEnabled", setPopupBlockingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setPrivateBrowsingEnabled", setPrivateBrowsingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "setXSSAuditorEnabled", setXSSAuditorEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "setFrameSetFlatteningEnabled", setFrameSetFlatteningEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setSelectTrailingWhitespaceEnabled", setSelectTrailingWhitespaceEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setSmartInsertDeleteEnabled", setSmartInsertDeleteEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSpatialNavigationEnabled", setSpatialNavigationEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setStopProvisionalFrameLoads", setStopProvisionalFrameLoadsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setTabKeyCyclesThroughElements", setTabKeyCyclesThroughElementsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setTimelineProfilingEnabled", setTimelineProfilingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setUseDashboardCompatibilityMode", setUseDashboardCompatibilityModeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setUserStyleSheetEnabled", setUserStyleSheetEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setUserStyleSheetLocation", setUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWebViewEditable", setWebViewEditableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setWillSendRequestClearHeader", setWillSendRequestClearHeaderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setWillSendRequestReturnsNull", setWillSendRequestReturnsNullCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setWillSendRequestReturnsNullOnRedirect", setWillSendRequestReturnsNullOnRedirectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setWindowIsKey", setWindowIsKeyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setXSSAuditorEnabled", setXSSAuditorEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "showWebInspector", showWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "testOnscreen", testOnscreenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "testRepaint", testRepaintCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "waitForPolicyDelegate", waitForPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "windowCount", windowCountCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
- { "whiteListAccessFromOrigin", whiteListAccessFromOriginCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "addOriginAccessWhitelistEntry", addOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setScrollbarPolicy", setScrollbarPolicyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "authenticateSession", authenticateSessionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ 0, 0, 0 }
};
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h
index 3add32a..198a9f3 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.h
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.h
@@ -31,9 +31,10 @@
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSRetainPtr.h>
-#include <wtf/RefCounted.h>
+#include <set>
#include <string>
#include <vector>
+#include <wtf/RefCounted.h>
class LayoutTestController : public RefCounted<LayoutTestController> {
public:
@@ -46,6 +47,7 @@ public:
void clearAllDatabases();
void clearBackForwardList();
void clearPersistentUserStyleSheet();
+ bool callShouldCloseOnWebView();
JSStringRef copyDecodedHostName(JSStringRef name);
JSStringRef copyEncodedHostName(JSStringRef name);
void disableImageLoading();
@@ -55,6 +57,7 @@ public:
JSRetainPtr<JSStringRef> counterValueForElementById(JSStringRef id);
bool isCommandEnabled(JSStringRef name);
void keepWebHistory();
+ JSValueRef computedStyleIncludingVisitedInfo(JSContextRef, JSValueRef);
void notifyDone();
int numberOfPages(float pageWidthInPixels, float pageHeightInPixels);
void overridePreference(JSStringRef key, JSStringRef value);
@@ -91,7 +94,9 @@ public:
void setUserStyleSheetEnabled(bool flag);
void setUserStyleSheetLocation(JSStringRef path);
void setXSSAuditorEnabled(bool flag);
- void setFrameSetFlatteningEnabled(bool enable);
+ void setFrameFlatteningEnabled(bool enable);
+ void setSpatialNavigationEnabled(bool enable);
+ void setScrollbarPolicy(JSStringRef orientation, JSStringRef policy);
void waitForPolicyDelegate();
size_t webHistoryItemCount();
@@ -185,6 +190,9 @@ public:
void setWaitToDump(bool waitToDump);
void waitToDumpWatchdogTimerFired();
+ const std::set<std::string>& willSendRequestClearHeaders() const { return m_willSendRequestClearHeaders; }
+ void setWillSendRequestClearHeader(std::string header) { m_willSendRequestClearHeaders.insert(header); }
+
bool willSendRequestReturnsNull() const { return m_willSendRequestReturnsNull; }
void setWillSendRequestReturnsNull(bool returnsNull) { m_willSendRequestReturnsNull = returnsNull; }
@@ -217,7 +225,8 @@ public:
bool sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId);
unsigned numberOfActiveAnimations() const;
- void whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+ void addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
+ void removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
void addUserScript(JSStringRef source, bool runAtStart);
void addUserStyleSheet(JSStringRef source);
@@ -226,6 +235,7 @@ public:
bool isGeolocationPermissionSet() const { return m_isGeolocationPermissionSet; }
bool geolocationPermission() const { return m_geolocationPermission; }
+ void setDeveloperExtrasEnabled(bool);
void showWebInspector();
void closeWebInspector();
void setTimelineProfilingEnabled(bool enabled);
@@ -233,10 +243,20 @@ public:
void evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script);
void setPOSIXLocale(JSStringRef locale);
-
+
+ void setWebViewEditable(bool);
+
// The following API test functions should probably be moved to platform-specific
// unit tests outside of DRT once they exist.
void apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL);
+ void apiTestGoToCurrentBackForwardItem();
+
+ // Simulate a request an embedding application could make, populating per-session credential storage.
+ void authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password);
+
+ JSRetainPtr<JSStringRef> layerTreeAsText() const;
+
+ JSRetainPtr<JSStringRef> markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const;
static const unsigned maxViewWidth;
static const unsigned maxViewHeight;
@@ -282,6 +302,8 @@ private:
std::string m_authenticationPassword;
std::string m_testPathOrURL;
std::string m_expectedPixelHash; // empty string if no hash
+
+ std::set<std::string> m_willSendRequestClearHeaders;
// origins which have been granted desktop notification access
std::vector<JSStringRef> m_desktopNotificationAllowedOrigins;
diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp
index 8e278f5..7d388d0 100644
--- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp
+++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2009 Holger Hans Peter Freyther
+ * Copyright (C) 2010 Collabora Ltd.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -134,6 +135,7 @@ enum {
ID_PROPERTY_PRIVATE_BROWSING_ENABLED,
ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED,
ID_PROPERTY_THROW_EXCEPTION_PROPERTY,
+ ID_LAST_SET_WINDOW_ARGUMENTS,
NUM_PROPERTY_IDENTIFIERS
};
@@ -147,7 +149,8 @@ static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
"returnErrorFromNewStream",
"privateBrowsingEnabled",
"cachedPrivateBrowsingEnabled",
- "testThrowExceptionProperty"
+ "testThrowExceptionProperty",
+ "lastSetWindowArguments"
};
enum {
@@ -181,6 +184,8 @@ enum {
ID_GET_AND_FORGET_REMEMBERED_OBJECT,
ID_REF_COUNT,
ID_SET_STATUS,
+ ID_RESIZE_TO,
+ ID_NORMALIZE,
NUM_METHOD_IDENTIFIERS
};
@@ -215,7 +220,9 @@ static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
"getRememberedObject",
"getAndForgetRememberedObject",
"refCount",
- "setStatus"
+ "setStatus",
+ "resizeTo",
+ "normalize"
};
static NPUTF8* createCStringFromNPVariant(const NPVariant* variant)
@@ -283,7 +290,15 @@ static bool pluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* resul
} else if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS");
return true;
+ } else if (name == pluginPropertyIdentifiers[ID_LAST_SET_WINDOW_ARGUMENTS]) {
+ char* buf = static_cast<char*>(browser->memalloc(256));
+ snprintf(buf, 256, "x: %d, y: %d, width: %u, height: %u, clipRect: (%u, %u, %u, %u)", (int)plugin->lastWindow.x, (int)plugin->lastWindow.y, (unsigned)plugin->lastWindow.width, (unsigned)plugin->lastWindow.height,
+ plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.top, plugin->lastWindow.clipRect.right - plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.bottom - plugin->lastWindow.clipRect.top);
+
+ STRINGZ_TO_NPVARIANT(buf, *result);
+ return true;
}
+
return false;
}
@@ -405,8 +420,8 @@ static bool testCallback(PluginObject* obj, const NPVariant* args, uint32_t argC
free(callbackString);
NPVariant browserResult;
- browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult);
- browser->releasevariantvalue(&browserResult);
+ if (browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult))
+ browser->releasevariantvalue(&browserResult);
browser->releaseobject(windowScriptObject);
@@ -519,8 +534,8 @@ static bool testEnumerate(PluginObject* obj, const NPVariant* args, uint32_t arg
NPVariant args[1];
STRINGZ_TO_NPVARIANT(string, args[0]);
NPVariant browserResult;
- browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult);
- browser->releasevariantvalue(&browserResult);
+ if (browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult))
+ browser->releasevariantvalue(&browserResult);
browser->memfree(string);
}
@@ -710,8 +725,10 @@ bool testDocumentOpen(NPP npp)
NPVariant docVariant;
browser->getproperty(npp, windowObject, documentId, &docVariant);
- if (docVariant.type != NPVariantType_Object)
+ if (docVariant.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
return false;
+ }
NPObject *documentObject = NPVARIANT_TO_OBJECT(docVariant);
@@ -720,17 +737,25 @@ bool testDocumentOpen(NPP npp)
STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
NPVariant result;
- browser->invoke(npp, documentObject, openId, openArgs, 2, &result);
+ if (!browser->invoke(npp, documentObject, openId, openArgs, 2, &result)) {
+ browser->releaseobject(windowObject);
+ browser->releaseobject(documentObject);
+ return false;
+ }
+
browser->releaseobject(documentObject);
- if (result.type == NPVariantType_Object) {
- pluginLogWithWindowObjectVariableArgs(windowObject, npp, "DOCUMENT OPEN SUCCESS");
- notifyTestCompletion(npp, result.value.objectValue);
- browser->releaseobject(result.value.objectValue);
- return true;
+ if (result.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
+ browser->releasevariantvalue(&result);
+ return false;
}
- return false;
+ pluginLogWithWindowObjectVariableArgs(windowObject, npp, "DOCUMENT OPEN SUCCESS");
+ notifyTestCompletion(npp, result.value.objectValue);
+ browser->releaseobject(result.value.objectValue);
+ browser->releaseobject(windowObject);
+ return true;
}
bool testWindowOpen(NPP npp)
@@ -747,14 +772,22 @@ bool testWindowOpen(NPP npp)
STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
NPVariant result;
- browser->invoke(npp, windowObject, openId, openArgs, 2, &result);
- if (result.type == NPVariantType_Object) {
- pluginLogWithWindowObjectVariableArgs(windowObject, npp, "WINDOW OPEN SUCCESS");
- notifyTestCompletion(npp, result.value.objectValue);
- browser->releaseobject(result.value.objectValue);
- return true;
+ if (!browser->invoke(npp, windowObject, openId, openArgs, 2, &result)) {
+ browser->releaseobject(windowObject);
+ return false;
}
- return false;
+
+ if (result.type != NPVariantType_Object) {
+ browser->releaseobject(windowObject);
+ browser->releasevariantvalue(&result);
+ return false;
+ }
+
+ pluginLogWithWindowObjectVariableArgs(windowObject, npp, "WINDOW OPEN SUCCESS");
+ notifyTestCompletion(npp, result.value.objectValue);
+ browser->releaseobject(result.value.objectValue);
+ browser->releaseobject(windowObject);
+ return true;
}
static bool testSetStatus(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
@@ -771,7 +804,40 @@ static bool testSetStatus(PluginObject* obj, const NPVariant* args, uint32_t arg
return true;
}
-static NPObject* rememberedObject;
+static bool testResizeTo(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ VOID_TO_NPVARIANT(*result);
+
+ NPObject* windowObject;
+ if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
+ return false;
+
+ NPVariant callResult;
+ if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("resizePlugin"), args, argCount, &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ // Force layout.
+ if (browser->getproperty(obj->npp, windowObject, browser->getstringidentifier("pageYOffset"), &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ return true;
+}
+
+static bool normalizeOverride(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ VOID_TO_NPVARIANT(*result);
+
+ NPObject* windowObject;
+ if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject))
+ return false;
+
+ NPVariant callResult;
+ if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("pluginCallback"), args, argCount, &callResult))
+ browser->releasevariantvalue(&callResult);
+
+ return true;
+}
+
static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result)
{
@@ -834,21 +900,21 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a
browser->setproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), &args[2]);
return true;
} else if (name == pluginMethodIdentifiers[ID_REMEMBER]) {
- if (rememberedObject)
- browser->releaseobject(rememberedObject);
- rememberedObject = NPVARIANT_TO_OBJECT(args[0]);
- browser->retainobject(rememberedObject);
+ if (plugin->rememberedObject)
+ browser->releaseobject(plugin->rememberedObject);
+ plugin->rememberedObject = NPVARIANT_TO_OBJECT(args[0]);
+ browser->retainobject(plugin->rememberedObject);
VOID_TO_NPVARIANT(*result);
return true;
} else if (name == pluginMethodIdentifiers[ID_GET_REMEMBERED_OBJECT]) {
- assert(rememberedObject);
- browser->retainobject(rememberedObject);
- OBJECT_TO_NPVARIANT(rememberedObject, *result);
+ assert(plugin->rememberedObject);
+ browser->retainobject(plugin->rememberedObject);
+ OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
return true;
} else if (name == pluginMethodIdentifiers[ID_GET_AND_FORGET_REMEMBERED_OBJECT]) {
- assert(rememberedObject);
- OBJECT_TO_NPVARIANT(rememberedObject, *result);
- rememberedObject = 0;
+ assert(plugin->rememberedObject);
+ OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result);
+ plugin->rememberedObject = 0;
return true;
} else if (name == pluginMethodIdentifiers[ID_REF_COUNT]) {
uint32_t refCount = NPVARIANT_TO_OBJECT(args[0])->referenceCount;
@@ -856,6 +922,10 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a
return true;
} else if (name == pluginMethodIdentifiers[ID_SET_STATUS])
return testSetStatus(plugin, args, argCount, result);
+ else if (name == pluginMethodIdentifiers[ID_RESIZE_TO])
+ return testResizeTo(plugin, args, argCount, result);
+ else if (name == pluginMethodIdentifiers[ID_NORMALIZE])
+ return normalizeOverride(plugin, args, argCount, result);
return false;
}
@@ -870,6 +940,7 @@ static void pluginInvalidate(NPObject* header)
{
PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
plugin->testObject = 0;
+ plugin->rememberedObject = 0;
}
static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
@@ -883,11 +954,13 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
newInstance->npp = npp;
newInstance->testObject = browser->createobject(npp, getTestClass());
+ newInstance->rememberedObject = 0;
newInstance->eventLogging = FALSE;
newInstance->onStreamLoad = 0;
newInstance->onStreamDestroy = 0;
newInstance->onDestroy = 0;
newInstance->onURLNotify = 0;
+ newInstance->onSetWindow = 0;
newInstance->logDestroy = FALSE;
newInstance->logSetWindow = FALSE;
newInstance->returnErrorFromNewStream = FALSE;
@@ -900,6 +973,7 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
newInstance->testDocumentOpenInDestroyStream = FALSE;
newInstance->testWindowOpen = FALSE;
+ newInstance->testKeyboardFocusForPlugins = FALSE;
return (NPObject*)newInstance;
}
@@ -909,6 +983,8 @@ static void pluginDeallocate(NPObject* header)
PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
if (plugin->testObject)
browser->releaseobject(plugin->testObject);
+ if (plugin->rememberedObject)
+ browser->releaseobject(plugin->rememberedObject);
free(plugin->firstUrl);
free(plugin->firstHeaders);
@@ -947,8 +1023,8 @@ void handleCallback(PluginObject* object, const char *url, NPReason reason, void
NULL_TO_NPVARIANT(args[1]);
NPVariant browserResult;
- browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult);
- browser->releasevariantvalue(&browserResult);
+ if (browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult))
+ browser->releasevariantvalue(&browserResult);
free(strHdr);
}
diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h
index 157a1d2..2ae4dbf 100644
--- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h
+++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h
@@ -37,13 +37,16 @@ typedef struct {
NPBool returnErrorFromNewStream;
NPBool cachedPrivateBrowsingMode;
NPObject* testObject;
+ NPObject* rememberedObject;
NPStream* stream;
NPBool testDocumentOpenInDestroyStream;
NPBool testWindowOpen;
+ NPBool testKeyboardFocusForPlugins;
char* onStreamLoad;
char* onStreamDestroy;
char* onDestroy;
char* onURLNotify;
+ char* onSetWindow;
char* firstUrl;
char* firstHeaders;
char* lastUrl;
@@ -51,6 +54,7 @@ typedef struct {
#ifdef XP_MACOSX
NPEventModel eventModel;
#endif
+ NPWindow lastWindow;
} PluginObject;
extern NPClass *getPluginClass(void);
diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp
index 8ef228a..a552774 100644
--- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp
+++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp
@@ -93,6 +93,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch
else if (strcasecmp(argn[i], "src") == 0 &&
strcasecmp(argv[i], "data:application/x-webkit-test-netscape,returnerrorfromnewstream") == 0)
obj->returnErrorFromNewStream = TRUE;
+ else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow)
+ obj->onSetWindow = strdup(argv[i]);
else if (strcasecmp(argn[i], "logfirstsetwindow") == 0)
obj->logSetWindow = TRUE;
else if (strcasecmp(argn[i], "testnpruntime") == 0)
@@ -111,6 +113,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch
obj->testDocumentOpenInDestroyStream = TRUE;
else if (strcasecmp(argn[i], "testwindowopen") == 0)
obj->testWindowOpen = TRUE;
+ else if (strcasecmp(argn[i], "src") == 0 && strstr(argv[i], "plugin-document-has-focus.pl"))
+ obj->testKeyboardFocusForPlugins = TRUE;
}
#ifndef NP_NO_CARBON
@@ -160,6 +164,9 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save)
if (obj->onURLNotify)
free(obj->onURLNotify);
+
+ if (obj->onSetWindow)
+ free(obj->onSetWindow);
if (obj->logDestroy)
pluginLog(instance, "NPP_Destroy");
@@ -174,15 +181,25 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window)
PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
if (obj) {
+ obj->lastWindow = *window;
+
if (obj->logSetWindow) {
pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height);
- obj->logSetWindow = false;
+ obj->logSetWindow = FALSE;
}
+ if (obj->onSetWindow)
+ executeScript(obj, obj->onSetWindow);
+
if (obj->testWindowOpen) {
testWindowOpen(instance);
obj->testWindowOpen = FALSE;
}
+
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = true;
+ executeScript(obj, "eventSender.keyDown('A');");
+ }
}
return NPERR_NO_ERROR;
@@ -275,6 +292,11 @@ static int16_t handleEventCarbon(NPP instance, PluginObject* obj, EventRecord* e
break;
case keyUp:
pluginLog(instance, "keyUp '%c'", (char)(event->message & 0xFF));
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "layoutTestController.notifyDone();");
+ }
break;
case autoKey:
pluginLog(instance, "autoKey '%c'", (char)(event->message & 0xFF));
@@ -338,7 +360,21 @@ static int16_t handleEventCocoa(NPP instance, PluginObject* obj, NPCocoaEvent* e
return 1;
case NPCocoaEventKeyDown:
+ if (event->data.key.characters)
+ pluginLog(instance, "keyDown '%c'", CFStringGetCharacterAtIndex(reinterpret_cast<CFStringRef>(event->data.key.characters), 0));
+ return 1;
+
case NPCocoaEventKeyUp:
+ if (event->data.key.characters) {
+ pluginLog(instance, "keyUp '%c'", CFStringGetCharacterAtIndex(reinterpret_cast<CFStringRef>(event->data.key.characters), 0));
+ if (obj->testKeyboardFocusForPlugins) {
+ obj->eventLogging = false;
+ obj->testKeyboardFocusForPlugins = FALSE;
+ executeScript(obj, "layoutTestController.notifyDone();");
+ }
+ }
+ return 1;
+
case NPCocoaEventFlagsChanged:
return 1;
diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp
new file mode 100644
index 0000000..afe850c
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityController.h"
+
+#include "TestShell.h"
+#include "public/WebAccessibilityCache.h"
+#include "public/WebAccessibilityObject.h"
+#include "public/WebFrame.h"
+#include "public/WebString.h"
+#include "public/WebView.h"
+
+using namespace WebKit;
+
+AccessibilityController::AccessibilityController(TestShell* shell)
+ : m_shell(shell)
+{
+
+ bindMethod("logFocusEvents",
+ &AccessibilityController::logFocusEventsCallback);
+ bindMethod("logScrollingStartEvents",
+ &AccessibilityController::logScrollingStartEventsCallback);
+
+ bindProperty("focusedElement",
+ &AccessibilityController::focusedElementGetterCallback);
+ bindProperty("rootElement",
+ &AccessibilityController::rootElementGetterCallback);
+
+ bindFallbackMethod(&AccessibilityController::fallbackCallback);
+}
+
+void AccessibilityController::bindToJavascript(WebFrame* frame, const WebString& classname)
+{
+ WebAccessibilityCache::enableAccessibility();
+ CppBoundClass::bindToJavascript(frame, classname);
+}
+
+void AccessibilityController::reset()
+{
+ m_rootElement = WebAccessibilityObject();
+ m_focusedElement = WebAccessibilityObject();
+ m_elements.clear();
+}
+
+void AccessibilityController::setFocusedElement(const WebAccessibilityObject& focusedElement)
+{
+ m_focusedElement = focusedElement;
+}
+
+AccessibilityUIElement* AccessibilityController::getFocusedElement()
+{
+ if (m_focusedElement.isNull())
+ m_focusedElement = m_shell->webView()->accessibilityObject();
+ return m_elements.create(m_focusedElement);
+}
+
+AccessibilityUIElement* AccessibilityController::getRootElement()
+{
+ if (m_rootElement.isNull())
+ m_rootElement = m_shell->webView()->accessibilityObject();
+ return m_elements.createRoot(m_rootElement);
+}
+
+void AccessibilityController::logFocusEventsCallback(const CppArgumentList&, CppVariant* result)
+{
+ // As of r49031, this is not being used upstream.
+ result->setNull();
+}
+
+void AccessibilityController::logScrollingStartEventsCallback(const CppArgumentList&, CppVariant* result)
+{
+ // As of r49031, this is not being used upstream.
+ result->setNull();
+}
+
+void AccessibilityController::focusedElementGetterCallback(CppVariant* result)
+{
+ result->set(*(getFocusedElement()->getAsCppVariant()));
+}
+
+void AccessibilityController::rootElementGetterCallback(CppVariant* result)
+{
+ result->set(*(getRootElement()->getAsCppVariant()));
+}
+
+void AccessibilityController::fallbackCallback(const CppArgumentList&, CppVariant* result)
+{
+ printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on "
+ "AccessibilityController\n");
+ result->setNull();
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h
new file mode 100644
index 0000000..3cde7cc
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityController_h
+#define AccessibilityController_h
+
+#include "AccessibilityUIElement.h"
+#include "CppBoundClass.h"
+
+namespace WebKit {
+class WebAccessibilityObject;
+class WebFrame;
+}
+
+class TestShell;
+
+class AccessibilityController : public CppBoundClass {
+public:
+ explicit AccessibilityController(TestShell*);
+
+ // Shadow to include accessibility initialization.
+ void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname);
+ void reset();
+
+ void setFocusedElement(const WebKit::WebAccessibilityObject&);
+ AccessibilityUIElement* getFocusedElement();
+ AccessibilityUIElement* getRootElement();
+
+private:
+ // Bound methods and properties
+ void logFocusEventsCallback(const CppArgumentList&, CppVariant*);
+ void logScrollingStartEventsCallback(const CppArgumentList&, CppVariant*);
+ void fallbackCallback(const CppArgumentList&, CppVariant*);
+
+ void focusedElementGetterCallback(CppVariant*);
+ void rootElementGetterCallback(CppVariant*);
+
+ WebKit::WebAccessibilityObject m_focusedElement;
+ WebKit::WebAccessibilityObject m_rootElement;
+
+ AccessibilityUIElementList m_elements;
+
+ TestShell* m_shell;
+};
+
+#endif // AccessibilityController_h
diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp
new file mode 100644
index 0000000..8698e25
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AccessibilityUIElement.h"
+
+#include "public/WebAccessibilityObject.h"
+#include "public/WebCString.h"
+#include "public/WebString.h"
+#include <wtf/Assertions.h>
+
+using namespace WebKit;
+using namespace std;
+
+// Map role value to string, matching Safari/Mac platform implementation to
+// avoid rebaselining layout tests.
+static string roleToString(WebAccessibilityRole role)
+{
+ string result = "AXRole: AX";
+ switch (role) {
+ case WebAccessibilityRoleButton:
+ return result.append("Button");
+ case WebAccessibilityRoleRadioButton:
+ return result.append("RadioButton");
+ case WebAccessibilityRoleCheckBox:
+ return result.append("CheckBox");
+ case WebAccessibilityRoleSlider:
+ return result.append("Slider");
+ case WebAccessibilityRoleTabGroup:
+ return result.append("TabGroup");
+ case WebAccessibilityRoleTextField:
+ return result.append("TextField");
+ case WebAccessibilityRoleStaticText:
+ return result.append("StaticText");
+ case WebAccessibilityRoleTextArea:
+ return result.append("TextArea");
+ case WebAccessibilityRoleScrollArea:
+ return result.append("ScrollArea");
+ case WebAccessibilityRolePopUpButton:
+ return result.append("PopUpButton");
+ case WebAccessibilityRoleMenuButton:
+ return result.append("MenuButton");
+ case WebAccessibilityRoleTable:
+ return result.append("Table");
+ case WebAccessibilityRoleApplication:
+ return result.append("Application");
+ case WebAccessibilityRoleGroup:
+ return result.append("Group");
+ case WebAccessibilityRoleRadioGroup:
+ return result.append("RadioGroup");
+ case WebAccessibilityRoleList:
+ return result.append("List");
+ case WebAccessibilityRoleScrollBar:
+ return result.append("ScrollBar");
+ case WebAccessibilityRoleValueIndicator:
+ return result.append("ValueIndicator");
+ case WebAccessibilityRoleImage:
+ return result.append("Image");
+ case WebAccessibilityRoleMenuBar:
+ return result.append("MenuBar");
+ case WebAccessibilityRoleMenu:
+ return result.append("Menu");
+ case WebAccessibilityRoleMenuItem:
+ return result.append("MenuItem");
+ case WebAccessibilityRoleColumn:
+ return result.append("Column");
+ case WebAccessibilityRoleRow:
+ return result.append("Row");
+ case WebAccessibilityRoleToolbar:
+ return result.append("Toolbar");
+ case WebAccessibilityRoleBusyIndicator:
+ return result.append("BusyIndicator");
+ case WebAccessibilityRoleProgressIndicator:
+ return result.append("ProgressIndicator");
+ case WebAccessibilityRoleWindow:
+ return result.append("Window");
+ case WebAccessibilityRoleDrawer:
+ return result.append("Drawer");
+ case WebAccessibilityRoleSystemWide:
+ return result.append("SystemWide");
+ case WebAccessibilityRoleOutline:
+ return result.append("Outline");
+ case WebAccessibilityRoleIncrementor:
+ return result.append("Incrementor");
+ case WebAccessibilityRoleBrowser:
+ return result.append("Browser");
+ case WebAccessibilityRoleComboBox:
+ return result.append("ComboBox");
+ case WebAccessibilityRoleSplitGroup:
+ return result.append("SplitGroup");
+ case WebAccessibilityRoleSplitter:
+ return result.append("Splitter");
+ case WebAccessibilityRoleColorWell:
+ return result.append("ColorWell");
+ case WebAccessibilityRoleGrowArea:
+ return result.append("GrowArea");
+ case WebAccessibilityRoleSheet:
+ return result.append("Sheet");
+ case WebAccessibilityRoleHelpTag:
+ return result.append("HelpTag");
+ case WebAccessibilityRoleMatte:
+ return result.append("Matte");
+ case WebAccessibilityRoleRuler:
+ return result.append("Ruler");
+ case WebAccessibilityRoleRulerMarker:
+ return result.append("RulerMarker");
+ case WebAccessibilityRoleLink:
+ return result.append("Link");
+ case WebAccessibilityRoleDisclosureTriangle:
+ return result.append("DisclosureTriangle");
+ case WebAccessibilityRoleGrid:
+ return result.append("Grid");
+ case WebAccessibilityRoleCell:
+ return result.append("Cell");
+ case WebAccessibilityRoleColumnHeader:
+ return result.append("ColumnHeader");
+ case WebAccessibilityRoleRowHeader:
+ return result.append("RowHeader");
+ case WebAccessibilityRoleWebCoreLink:
+ // Maps to Link role.
+ return result.append("Link");
+ case WebAccessibilityRoleImageMapLink:
+ return result.append("ImageMapLink");
+ case WebAccessibilityRoleImageMap:
+ return result.append("ImageMap");
+ case WebAccessibilityRoleListMarker:
+ return result.append("ListMarker");
+ case WebAccessibilityRoleWebArea:
+ return result.append("WebArea");
+ case WebAccessibilityRoleHeading:
+ return result.append("Heading");
+ case WebAccessibilityRoleListBox:
+ return result.append("ListBox");
+ case WebAccessibilityRoleListBoxOption:
+ return result.append("ListBoxOption");
+ case WebAccessibilityRoleTableHeaderContainer:
+ return result.append("TableHeaderContainer");
+ case WebAccessibilityRoleDefinitionListTerm:
+ return result.append("DefinitionListTerm");
+ case WebAccessibilityRoleDefinitionListDefinition:
+ return result.append("DefinitionListDefinition");
+ case WebAccessibilityRoleAnnotation:
+ return result.append("Annotation");
+ case WebAccessibilityRoleSliderThumb:
+ return result.append("SliderThumb");
+ case WebAccessibilityRoleLandmarkApplication:
+ return result.append("LandmarkApplication");
+ case WebAccessibilityRoleLandmarkBanner:
+ return result.append("LandmarkBanner");
+ case WebAccessibilityRoleLandmarkComplementary:
+ return result.append("LandmarkComplementary");
+ case WebAccessibilityRoleLandmarkContentInfo:
+ return result.append("LandmarkContentInfo");
+ case WebAccessibilityRoleLandmarkMain:
+ return result.append("LandmarkMain");
+ case WebAccessibilityRoleLandmarkNavigation:
+ return result.append("LandmarkNavigation");
+ case WebAccessibilityRoleLandmarkSearch:
+ return result.append("LandmarkSearch");
+ case WebAccessibilityRoleApplicationLog:
+ return result.append("ApplicationLog");
+ case WebAccessibilityRoleApplicationMarquee:
+ return result.append("ApplicationMarquee");
+ case WebAccessibilityRoleApplicationStatus:
+ return result.append("ApplicationStatus");
+ case WebAccessibilityRoleApplicationTimer:
+ return result.append("ApplicationTimer");
+ case WebAccessibilityRoleDocument:
+ return result.append("Document");
+ case WebAccessibilityRoleDocumentArticle:
+ return result.append("DocumentArticle");
+ case WebAccessibilityRoleDocumentNote:
+ return result.append("DocumentNote");
+ case WebAccessibilityRoleDocumentRegion:
+ return result.append("DocumentRegion");
+ case WebAccessibilityRoleUserInterfaceTooltip:
+ return result.append("UserInterfaceTooltip");
+ default:
+ // Also matches WebAccessibilityRoleUnknown.
+ return result.append("Unknown");
+ }
+}
+
+string getDescription(const WebAccessibilityObject& object)
+{
+ string description = object.accessibilityDescription().utf8();
+ return description.insert(0, "AXDescription: ");
+}
+
+string getRole(const WebAccessibilityObject& object)
+{
+ return roleToString(object.roleValue());
+}
+
+string getTitle(const WebAccessibilityObject& object)
+{
+ string title = object.title().utf8();
+ return title.insert(0, "AXTitle: ");
+}
+
+string getAttributes(const WebAccessibilityObject& object)
+{
+ // FIXME: Concatenate all attributes of the AccessibilityObject.
+ string attributes(getTitle(object));
+ attributes.append("\n");
+ attributes.append(getRole(object));
+ attributes.append("\n");
+ attributes.append(getDescription(object));
+ return attributes;
+}
+
+
+// Collects attributes into a string, delimited by dashes. Used by all methods
+// that output lists of attributes: attributesOfLinkedUIElementsCallback,
+// AttributesOfChildrenCallback, etc.
+class AttributesCollector {
+public:
+ void collectAttributes(const WebAccessibilityObject& object)
+ {
+ m_attributes.append("\n------------\n");
+ m_attributes.append(getAttributes(object));
+ }
+
+ string attributes() const { return m_attributes; }
+
+private:
+ string m_attributes;
+};
+
+AccessibilityUIElement::AccessibilityUIElement(const WebAccessibilityObject& object, Factory* factory)
+ : m_accessibilityObject(object)
+ , m_factory(factory)
+{
+
+ ASSERT(factory);
+
+ bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback);
+ bindMethod("attributesOfLinkedUIElements",
+ &AccessibilityUIElement::attributesOfLinkedUIElementsCallback);
+ bindMethod("attributesOfDocumentLinks",
+ &AccessibilityUIElement::attributesOfDocumentLinksCallback);
+ bindMethod("attributesOfChildren",
+ &AccessibilityUIElement::attributesOfChildrenCallback);
+ bindMethod("parameterizedAttributeNames",
+ &AccessibilityUIElement::parametrizedAttributeNamesCallback);
+ bindMethod("lineForIndex", &AccessibilityUIElement::lineForIndexCallback);
+ bindMethod("boundsForRange", &AccessibilityUIElement::boundsForRangeCallback);
+ bindMethod("stringForRange", &AccessibilityUIElement::stringForRangeCallback);
+ bindMethod("childAtIndex", &AccessibilityUIElement::childAtIndexCallback);
+ bindMethod("elementAtPoint", &AccessibilityUIElement::elementAtPointCallback);
+ bindMethod("attributesOfColumnHeaders",
+ &AccessibilityUIElement::attributesOfColumnHeadersCallback);
+ bindMethod("attributesOfRowHeaders",
+ &AccessibilityUIElement::attributesOfRowHeadersCallback);
+ bindMethod("attributesOfColumns",
+ &AccessibilityUIElement::attributesOfColumnsCallback);
+ bindMethod("attributesOfRows",
+ &AccessibilityUIElement::attributesOfRowsCallback);
+ bindMethod("attributesOfVisibleCells",
+ &AccessibilityUIElement::attributesOfVisibleCellsCallback);
+ bindMethod("attributesOfHeader",
+ &AccessibilityUIElement::attributesOfHeaderCallback);
+ bindMethod("indexInTable", &AccessibilityUIElement::indexInTableCallback);
+ bindMethod("rowIndexRange", &AccessibilityUIElement::rowIndexRangeCallback);
+ bindMethod("columnIndexRange",
+ &AccessibilityUIElement::columnIndexRangeCallback);
+ bindMethod("cellForColumnAndRow",
+ &AccessibilityUIElement::cellForColumnAndRowCallback);
+ bindMethod("titleUIElement", &AccessibilityUIElement::titleUIElementCallback);
+ bindMethod("setSelectedTextRange",
+ &AccessibilityUIElement::setSelectedTextRangeCallback);
+ bindMethod("attributeValue", &AccessibilityUIElement::attributeValueCallback);
+ bindMethod("isAttributeSettable",
+ &AccessibilityUIElement::isAttributeSettableCallback);
+ bindMethod("isActionSupported",
+ &AccessibilityUIElement::isActionSupportedCallback);
+ bindMethod("parentElement", &AccessibilityUIElement::parentElementCallback);
+ bindMethod("increment", &AccessibilityUIElement::incrementCallback);
+ bindMethod("decrement", &AccessibilityUIElement::decrementCallback);
+
+ bindProperty("role", &AccessibilityUIElement::roleGetterCallback);
+ bindProperty("subrole", &m_subrole);
+ bindProperty("title", &AccessibilityUIElement::titleGetterCallback);
+ bindProperty("description",
+ &AccessibilityUIElement::descriptionGetterCallback);
+ bindProperty("language", &m_language);
+ bindProperty("x", &m_x);
+ bindProperty("y", &m_y);
+ bindProperty("width", &m_width);
+ bindProperty("height", &m_height);
+ bindProperty("clickPointX", &m_clickPointX);
+ bindProperty("clickPointY", &m_clickPointY);
+ bindProperty("intValue", &m_intValue);
+ bindProperty("minValue", &m_minValue);
+ bindProperty("maxValue", &m_maxValue);
+ bindProperty("childrenCount",
+ &AccessibilityUIElement::childrenCountGetterCallback);
+ bindProperty("insertionPointLineNumber", &m_insertionPointLineNumber);
+ bindProperty("selectedTextRange", &m_selectedTextRange);
+ bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback);
+ bindProperty("isRequired", &m_isRequired);
+ bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback);
+ bindProperty("valueDescription", &m_valueDescription);
+
+ bindFallbackMethod(&AccessibilityUIElement::fallbackCallback);
+}
+
+AccessibilityUIElement* AccessibilityUIElement::getChildAtIndex(unsigned index)
+{
+ return m_factory->create(accessibilityObject().childAt(index));
+}
+
+void AccessibilityUIElement::allAttributesCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->set(getAttributes(accessibilityObject()));
+}
+
+void AccessibilityUIElement::attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfChildrenCallback(const CppArgumentList& arguments, CppVariant* result)
+{
+ AttributesCollector collector;
+ unsigned size = accessibilityObject().childCount();
+ for (unsigned i = 0; i < size; ++i)
+ collector.collectAttributes(accessibilityObject().childAt(i));
+ result->set(collector.attributes());
+}
+
+void AccessibilityUIElement::parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::lineForIndexCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::boundsForRangeCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::stringForRangeCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::childAtIndexCallback(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (!arguments.size() || !arguments[0].isNumber()) {
+ result->setNull();
+ return;
+ }
+
+ AccessibilityUIElement* child = getChildAtIndex(arguments[0].toInt32());
+ if (!child) {
+ result->setNull();
+ return;
+ }
+
+ result->set(*(child->getAsCppVariant()));
+}
+
+void AccessibilityUIElement::elementAtPointCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfColumnsCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfRowsCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributesOfHeaderCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::indexInTableCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::rowIndexRangeCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::columnIndexRangeCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::cellForColumnAndRowCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::titleUIElementCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::setSelectedTextRangeCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::attributeValueCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::isAttributeSettableCallback(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() < 1 && !arguments[0].isString()) {
+ result->setNull();
+ return;
+ }
+
+ string attribute = arguments[0].toString();
+ bool settable = false;
+ if (attribute == "AXValue")
+ settable = accessibilityObject().canSetValueAttribute();
+ result->set(settable);
+}
+
+void AccessibilityUIElement::isActionSupportedCallback(const CppArgumentList&, CppVariant* result)
+{
+ // This one may be really hard to implement.
+ // Not exposed by AccessibilityObject.
+ result->setNull();
+}
+
+void AccessibilityUIElement::parentElementCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::incrementCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::decrementCallback(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::fallbackCallback(const CppArgumentList &, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void AccessibilityUIElement::childrenCountGetterCallback(CppVariant* result)
+{
+ int count = 1; // Root object always has only one child, the WebView.
+ if (!isRoot())
+ count = accessibilityObject().childCount();
+ result->set(count);
+}
+
+void AccessibilityUIElement::descriptionGetterCallback(CppVariant* result)
+{
+ result->set(getDescription(accessibilityObject()));
+}
+
+void AccessibilityUIElement::isEnabledGetterCallback(CppVariant* result)
+{
+ result->set(accessibilityObject().isEnabled());
+}
+
+void AccessibilityUIElement::isSelectedGetterCallback(CppVariant* result)
+{
+ result->setNull();
+}
+
+void AccessibilityUIElement::roleGetterCallback(CppVariant* result)
+{
+ result->set(getRole(accessibilityObject()));
+}
+
+void AccessibilityUIElement::titleGetterCallback(CppVariant* result)
+{
+ result->set(getTitle(accessibilityObject()));
+}
+
+
+RootAccessibilityUIElement::RootAccessibilityUIElement(const WebAccessibilityObject &object, Factory *factory)
+ : AccessibilityUIElement(object, factory) { }
+
+AccessibilityUIElement* RootAccessibilityUIElement::getChildAtIndex(unsigned index)
+{
+ if (index)
+ return 0;
+
+ return factory()->create(accessibilityObject());
+}
+
+
+AccessibilityUIElementList ::~AccessibilityUIElementList()
+{
+ clear();
+}
+
+void AccessibilityUIElementList::clear()
+{
+ for (ElementList::iterator i = m_elements.begin(); i != m_elements.end(); ++i)
+ delete (*i);
+ m_elements.clear();
+}
+
+AccessibilityUIElement* AccessibilityUIElementList::create(const WebAccessibilityObject& object)
+{
+ if (object.isNull())
+ return 0;
+
+ AccessibilityUIElement* element = new AccessibilityUIElement(object, this);
+ m_elements.append(element);
+ return element;
+}
+
+AccessibilityUIElement* AccessibilityUIElementList::createRoot(const WebAccessibilityObject& object)
+{
+ AccessibilityUIElement* element = new RootAccessibilityUIElement(object, this);
+ m_elements.append(element);
+ return element;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h
new file mode 100644
index 0000000..df3f5b9
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityUIElement_h
+#define AccessibilityUIElement_h
+
+#include "CppBoundClass.h"
+#include "public/WebAccessibilityObject.h"
+#include <wtf/Vector.h>
+
+class AccessibilityUIElement : public CppBoundClass {
+public:
+ class Factory {
+ public:
+ virtual ~Factory() { }
+ virtual AccessibilityUIElement* create(const WebKit::WebAccessibilityObject&) = 0;
+ };
+
+ AccessibilityUIElement(const WebKit::WebAccessibilityObject&, Factory*);
+
+ virtual AccessibilityUIElement* getChildAtIndex(unsigned);
+ virtual bool isRoot() const { return false; }
+
+protected:
+ const WebKit::WebAccessibilityObject& accessibilityObject() const { return m_accessibilityObject; }
+ Factory* factory() const { return m_factory; }
+
+private:
+ // Bound methods and properties.
+ void allAttributesCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfChildrenCallback(const CppArgumentList&, CppVariant*);
+ void parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant*);
+ void lineForIndexCallback(const CppArgumentList&, CppVariant*);
+ void boundsForRangeCallback(const CppArgumentList&, CppVariant*);
+ void stringForRangeCallback(const CppArgumentList&, CppVariant*);
+ void childAtIndexCallback(const CppArgumentList&, CppVariant*);
+ void elementAtPointCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfColumnsCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfRowsCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant*);
+ void attributesOfHeaderCallback(const CppArgumentList&, CppVariant*);
+ void indexInTableCallback(const CppArgumentList&, CppVariant*);
+ void rowIndexRangeCallback(const CppArgumentList&, CppVariant*);
+ void columnIndexRangeCallback(const CppArgumentList&, CppVariant*);
+ void cellForColumnAndRowCallback(const CppArgumentList&, CppVariant*);
+ void titleUIElementCallback(const CppArgumentList&, CppVariant*);
+ void setSelectedTextRangeCallback(const CppArgumentList&, CppVariant*);
+ void attributeValueCallback(const CppArgumentList&, CppVariant*);
+ void isAttributeSettableCallback(const CppArgumentList&, CppVariant*);
+ void isActionSupportedCallback(const CppArgumentList&, CppVariant*);
+ void parentElementCallback(const CppArgumentList&, CppVariant*);
+ void incrementCallback(const CppArgumentList&, CppVariant*);
+ void decrementCallback(const CppArgumentList&, CppVariant*);
+ void fallbackCallback(const CppArgumentList&, CppVariant*);
+
+ void childrenCountGetterCallback(CppVariant*);
+ void descriptionGetterCallback(CppVariant*);
+ void isEnabledGetterCallback(CppVariant*);
+ void isSelectedGetterCallback(CppVariant*);
+ void roleGetterCallback(CppVariant*);
+ void titleGetterCallback(CppVariant*);
+
+ CppVariant m_subrole;
+ CppVariant m_language;
+ CppVariant m_x;
+ CppVariant m_y;
+ CppVariant m_width;
+ CppVariant m_height;
+ CppVariant m_clickPointX;
+ CppVariant m_clickPointY;
+ CppVariant m_intValue;
+ CppVariant m_minValue;
+ CppVariant m_maxValue;
+ CppVariant m_childrenCount;
+ CppVariant m_insertionPointLineNumber;
+ CppVariant m_selectedTextRange;
+ CppVariant m_isRequired;
+ CppVariant m_valueDescription;
+
+ WebKit::WebAccessibilityObject m_accessibilityObject;
+ Factory* m_factory;
+};
+
+
+class RootAccessibilityUIElement : public AccessibilityUIElement {
+public:
+ RootAccessibilityUIElement(const WebKit::WebAccessibilityObject&, Factory*);
+
+ virtual AccessibilityUIElement* getChildAtIndex(unsigned);
+ virtual bool isRoot() const { return true; }
+};
+
+
+// Provides simple lifetime management of the AccessibilityUIElement instances:
+// all AccessibilityUIElements ever created from the controller are stored in
+// a list and cleared explicitly.
+class AccessibilityUIElementList : public AccessibilityUIElement::Factory {
+public:
+ AccessibilityUIElementList() { }
+ virtual ~AccessibilityUIElementList();
+
+ void clear();
+ virtual AccessibilityUIElement* create(const WebKit::WebAccessibilityObject&);
+ AccessibilityUIElement* createRoot(const WebKit::WebAccessibilityObject&);
+
+private:
+ typedef Vector<AccessibilityUIElement*> ElementList;
+ ElementList m_elements;
+};
+
+#endif // AccessibilityUIElement_h
diff --git a/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp
new file mode 100644
index 0000000..839787a
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file contains definitions for CppBoundClass
+
+// Here's the control flow of a JS method getting forwarded to a class.
+// - Something calls our NPObject with a function like "Invoke".
+// - CppNPObject's static invoke() function forwards it to its attached
+// CppBoundClass's invoke() method.
+// - CppBoundClass has then overridden invoke() to look up the function
+// name in its internal map of methods, and then calls the appropriate
+// method.
+
+#include "config.h"
+#include "CppBoundClass.h"
+
+#include "public/WebBindings.h"
+#include "public/WebFrame.h"
+#include "public/WebString.h"
+#include <wtf/Assertions.h>
+#include <wtf/OwnPtr.h>
+
+using namespace WebKit;
+using namespace std;
+
+class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
+public:
+ CppVariantPropertyCallback(CppVariant* value) : m_value(value) { }
+
+ virtual bool getValue(CppVariant* value)
+ {
+ value->set(*m_value);
+ return true;
+ }
+
+ virtual bool setValue(const CppVariant& value)
+ {
+ m_value->set(value);
+ return true;
+ }
+
+private:
+ CppVariant* m_value;
+};
+
+class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
+public:
+ GetterPropertyCallback(CppBoundClass::GetterCallback* callback)
+ : m_callback(callback) { }
+
+ virtual bool getValue(CppVariant* value)
+ {
+ m_callback->run(value);
+ return true;
+ }
+
+ virtual bool setValue(const CppVariant& value) { return false; }
+
+private:
+ OwnPtr<CppBoundClass::GetterCallback> m_callback;
+};
+
+// Our special NPObject type. We extend an NPObject with a pointer to a
+// CppBoundClass, which is just a C++ interface that we forward all NPObject
+// callbacks to.
+struct CppNPObject {
+ NPObject parent; // This must be the first field in the struct.
+ CppBoundClass* boundClass;
+
+ //
+ // All following objects and functions are static, and just used to interface
+ // with NPObject/NPClass.
+ //
+
+ // An NPClass associates static functions of CppNPObject with the
+ // function pointers used by the JS runtime.
+ static NPClass npClass;
+
+ // Allocate a new NPObject with the specified class.
+ static NPObject* allocate(NPP, NPClass*);
+
+ // Free an object.
+ static void deallocate(NPObject*);
+
+ // Returns true if the C++ class associated with this NPObject exposes the
+ // given property. Called by the JS runtime.
+ static bool hasProperty(NPObject*, NPIdentifier);
+
+ // Returns true if the C++ class associated with this NPObject exposes the
+ // given method. Called by the JS runtime.
+ static bool hasMethod(NPObject*, NPIdentifier);
+
+ // If the given method is exposed by the C++ class associated with this
+ // NPObject, invokes it with the given arguments and returns a result. Otherwise,
+ // returns "undefined" (in the JavaScript sense). Called by the JS runtime.
+ static bool invoke(NPObject*, NPIdentifier,
+ const NPVariant* arguments, uint32_t argumentCount,
+ NPVariant* result);
+
+ // If the given property is exposed by the C++ class associated with this
+ // NPObject, returns its value. Otherwise, returns "undefined" (in the
+ // JavaScript sense). Called by the JS runtime.
+ static bool getProperty(NPObject*, NPIdentifier, NPVariant* result);
+
+ // If the given property is exposed by the C++ class associated with this
+ // NPObject, sets its value. Otherwise, does nothing. Called by the JS
+ // runtime.
+ static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value);
+};
+
+// Build CppNPObject's static function pointers into an NPClass, for use
+// in constructing NPObjects for the C++ classes.
+NPClass CppNPObject::npClass = {
+ NP_CLASS_STRUCT_VERSION,
+ CppNPObject::allocate,
+ CppNPObject::deallocate,
+ /* NPInvalidateFunctionPtr */ 0,
+ CppNPObject::hasMethod,
+ CppNPObject::invoke,
+ /* NPInvokeDefaultFunctionPtr */ 0,
+ CppNPObject::hasProperty,
+ CppNPObject::getProperty,
+ CppNPObject::setProperty,
+ /* NPRemovePropertyFunctionPtr */ 0
+};
+
+NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass)
+{
+ CppNPObject* obj = new CppNPObject;
+ // obj->parent will be initialized by the NPObject code calling this.
+ obj->boundClass = 0;
+ return &obj->parent;
+}
+
+void CppNPObject::deallocate(NPObject* npObj)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ delete obj;
+}
+
+bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ return obj->boundClass->hasMethod(ident);
+}
+
+bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ return obj->boundClass->hasProperty(ident);
+}
+
+bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident,
+ const NPVariant* arguments, uint32_t argumentCount,
+ NPVariant* result)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ return obj->boundClass->invoke(ident, arguments, argumentCount, result);
+}
+
+bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ return obj->boundClass->getProperty(ident, result);
+}
+
+bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value)
+{
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ return obj->boundClass->setProperty(ident, value);
+}
+
+CppBoundClass::~CppBoundClass()
+{
+ for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i)
+ delete i->second;
+
+ for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i)
+ delete i->second;
+
+ // Unregister ourselves if we were bound to a frame.
+ if (m_boundToFrame)
+ WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant));
+}
+
+bool CppBoundClass::hasMethod(NPIdentifier ident) const
+{
+ return m_methods.find(ident) != m_methods.end();
+}
+
+bool CppBoundClass::hasProperty(NPIdentifier ident) const
+{
+ return m_properties.find(ident) != m_properties.end();
+}
+
+bool CppBoundClass::invoke(NPIdentifier ident,
+ const NPVariant* arguments,
+ size_t argumentCount,
+ NPVariant* result) {
+ MethodList::const_iterator end = m_methods.end();
+ MethodList::const_iterator method = m_methods.find(ident);
+ Callback* callback;
+ if (method == end) {
+ if (!m_fallbackCallback.get()) {
+ VOID_TO_NPVARIANT(*result);
+ return false;
+ }
+ callback = m_fallbackCallback.get();
+ } else
+ callback = (*method).second;
+
+ // Build a CppArgumentList argument vector from the NPVariants coming in.
+ CppArgumentList cppArguments(argumentCount);
+ for (size_t i = 0; i < argumentCount; i++)
+ cppArguments[i].set(arguments[i]);
+
+ CppVariant cppResult;
+ callback->run(cppArguments, &cppResult);
+
+ cppResult.copyToNPVariant(result);
+ return true;
+}
+
+bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const
+{
+ PropertyList::const_iterator callback = m_properties.find(ident);
+ if (callback == m_properties.end()) {
+ VOID_TO_NPVARIANT(*result);
+ return false;
+ }
+
+ CppVariant cppValue;
+ if (!callback->second->getValue(&cppValue))
+ return false;
+ cppValue.copyToNPVariant(result);
+ return true;
+}
+
+bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value)
+{
+ PropertyList::iterator callback = m_properties.find(ident);
+ if (callback == m_properties.end())
+ return false;
+
+ CppVariant cppValue;
+ cppValue.set(*value);
+ return (*callback).second->setValue(cppValue);
+}
+
+void CppBoundClass::bindCallback(const string& name, Callback* callback)
+{
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ MethodList::iterator oldCallback = m_methods.find(ident);
+ if (oldCallback != m_methods.end()) {
+ delete oldCallback->second;
+ if (!callback) {
+ m_methods.remove(oldCallback);
+ return;
+ }
+ }
+
+ m_methods.set(ident, callback);
+}
+
+void CppBoundClass::bindGetterCallback(const string& name, GetterCallback* callback)
+{
+ PropertyCallback* propertyCallback = callback ? new GetterPropertyCallback(callback) : 0;
+ bindProperty(name, propertyCallback);
+}
+
+void CppBoundClass::bindProperty(const string& name, CppVariant* prop)
+{
+ PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0;
+ bindProperty(name, propertyCallback);
+}
+
+void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback)
+{
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ PropertyList::iterator oldCallback = m_properties.find(ident);
+ if (oldCallback != m_properties.end()) {
+ delete oldCallback->second;
+ if (!callback) {
+ m_properties.remove(oldCallback);
+ return;
+ }
+ }
+
+ m_properties.set(ident, callback);
+}
+
+bool CppBoundClass::isMethodRegistered(const string& name) const
+{
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ MethodList::const_iterator callback = m_methods.find(ident);
+ return callback != m_methods.end();
+}
+
+CppVariant* CppBoundClass::getAsCppVariant()
+{
+ if (!m_selfVariant.isObject()) {
+ // Create an NPObject using our static NPClass. The first argument (a
+ // plugin's instance handle) is passed through to the allocate function
+ // directly, and we don't use it, so it's ok to be 0.
+ NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass);
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
+ obj->boundClass = this;
+ m_selfVariant.set(npObj);
+ WebBindings::releaseObject(npObj); // CppVariant takes the reference.
+ }
+ ASSERT(m_selfVariant.isObject());
+ return &m_selfVariant;
+}
+
+void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname)
+{
+ // BindToWindowObject will take its own reference to the NPObject, and clean
+ // up after itself. It will also (indirectly) register the object with V8,
+ // so we must remember this so we can unregister it when we're destroyed.
+ frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()));
+ m_boundToFrame = true;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h
new file mode 100644
index 0000000..6cb638e
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org)
+ *
+ * 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.
+ */
+
+/*
+ CppBoundClass class:
+ This base class serves as a parent for C++ classes designed to be bound to
+ JavaScript objects.
+
+ Subclasses should define the constructor to build the property and method
+ lists needed to bind this class to a JS object. They should also declare
+ and define member variables and methods to be exposed to JS through
+ that object.
+*/
+
+#ifndef CppBoundClass_h
+#define CppBoundClass_h
+
+#include "CppVariant.h"
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebKit {
+class WebFrame;
+class WebString;
+}
+
+typedef Vector<CppVariant> CppArgumentList;
+
+// CppBoundClass lets you map Javascript method calls and property accesses
+// directly to C++ method calls and CppVariant* variable access.
+class CppBoundClass : public Noncopyable {
+public:
+ class PropertyCallback {
+ public:
+ virtual ~PropertyCallback() { }
+
+ // Sets |value| to the value of the property. Returns false in case of
+ // failure. |value| is always non-0.
+ virtual bool getValue(CppVariant* result) = 0;
+
+ // sets the property value to |value|. Returns false in case of failure.
+ virtual bool setValue(const CppVariant&) = 0;
+ };
+
+ // Callback class for "void function(CppVariant*)"
+ class GetterCallback {
+ public:
+ virtual ~GetterCallback() {}
+ virtual void run(CppVariant*) = 0;
+ };
+
+ // The constructor should call BindMethod, BindProperty, and
+ // SetFallbackMethod as needed to set up the methods, properties, and
+ // fallback method.
+ CppBoundClass() : m_boundToFrame(false) {}
+ virtual ~CppBoundClass();
+
+ // Return a CppVariant representing this class, for use with BindProperty().
+ // The variant type is guaranteed to be NPVariantType_Object.
+ CppVariant* getAsCppVariant();
+
+ // Given a WebFrame, BindToJavascript builds the NPObject that will represent
+ // the class and binds it to the frame's window under the given name. This
+ // should generally be called from the WebView delegate's
+ // WindowObjectCleared(). A class so bound will be accessible to JavaScript
+ // as window.<classname>. The owner of the CppBoundObject is responsible for
+ // keeping the object around while the frame is alive, and for destroying it
+ // afterwards.
+ void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname);
+
+ // Used by a test. Returns true if a method with name |name| exists,
+ // regardless of whether a fallback is registered.
+ bool isMethodRegistered(const std::string&) const;
+
+protected:
+ // Callback for "void function(const CppArguemntList&, CppVariant*)"
+ class Callback {
+ public:
+ virtual ~Callback() {}
+ virtual void run(const CppArgumentList&, CppVariant*) = 0;
+ };
+
+ // Callback for "void T::method(const CppArguemntList&, CppVariant*)"
+ template <class T> class MemberCallback : public Callback {
+ public:
+ typedef void (T::*MethodType)(const CppArgumentList&, CppVariant*);
+ MemberCallback(T* object, MethodType method)
+ : m_object(object)
+ , m_method(method) {}
+ virtual ~MemberCallback() {}
+
+ virtual void run(const CppArgumentList& arguments, CppVariant* result)
+ {
+ (m_object->*m_method)(arguments, result);
+ }
+ private:
+ T* m_object;
+ MethodType m_method;
+ };
+
+ // Callback class for "void T::method(CppVariant*)"
+ template <class T> class MemberGetterCallback : public GetterCallback {
+ public:
+ typedef void (T::*MethodType)(CppVariant*);
+ MemberGetterCallback(T* object, MethodType method)
+ : m_object(object)
+ , m_method(method) {}
+ virtual ~MemberGetterCallback() {}
+
+ virtual void run(CppVariant* result) { (m_object->*m_method)(result); }
+ private:
+ T* m_object;
+ MethodType m_method;
+ };
+
+ // Bind the Javascript method called the string parameter to the C++ method.
+ void bindCallback(const std::string&, Callback*);
+
+ // A wrapper for bindCallback, to simplify the common case of binding a
+ // method on the current object. Though not verified here, |method|
+ // must be a method of this CppBoundClass subclass.
+ template<class T>
+ void bindMethod(const std::string& name, void (T::*method)(const CppArgumentList&, CppVariant*))
+ {
+ Callback* callback = new MemberCallback<T>(static_cast<T*>(this), method);
+ bindCallback(name, callback);
+ }
+
+ // Bind Javascript property |name| to the C++ getter callback |callback|.
+ // This can be used to create read-only properties.
+ void bindGetterCallback(const std::string&, GetterCallback*);
+
+ // A wrapper for BindGetterCallback, to simplify the common case of binding a
+ // property on the current object. Though not verified here, |method|
+ // must be a method of this CppBoundClass subclass.
+ template<class T>
+ void bindProperty(const std::string& name, void (T::*method)(CppVariant*))
+ {
+ GetterCallback* callback = new MemberGetterCallback<T>(static_cast<T*>(this), method);
+ bindGetterCallback(name, callback);
+ }
+
+ // Bind the Javascript property called |name| to a CppVariant.
+ void bindProperty(const std::string&, CppVariant*);
+
+ // Bind Javascript property called |name| to a PropertyCallback.
+ // CppBoundClass assumes control over the life time of the callback.
+ void bindProperty(const std::string&, PropertyCallback*);
+
+ // Set the fallback callback, which is called when when a callback is
+ // invoked that isn't bound.
+ // If it is 0 (its default value), a JavaScript exception is thrown in
+ // that case (as normally expected). If non 0, the fallback method is
+ // invoked and the script continues its execution.
+ // Passing 0 clears out any existing binding.
+ // It is used for tests and should probably only be used in such cases
+ // as it may cause unexpected behaviors (a JavaScript object with a
+ // fallback always returns true when checked for a method's
+ // existence).
+ void bindFallbackCallback(Callback* fallbackCallback)
+ {
+ m_fallbackCallback.set(fallbackCallback);
+ }
+
+ // A wrapper for BindFallbackCallback, to simplify the common case of
+ // binding a method on the current object. Though not verified here,
+ // |method| must be a method of this CppBoundClass subclass.
+ // Passing 0 for |method| clears out any existing binding.
+ template<class T>
+ void bindFallbackMethod(void (T::*method)(const CppArgumentList&, CppVariant*))
+ {
+ if (method) {
+ Callback* callback = new MemberCallback<T>(static_cast<T*>(this), method);
+ bindFallbackCallback(callback);
+ } else
+ bindFallbackCallback(0);
+ }
+
+ // Some fields are protected because some tests depend on accessing them,
+ // but otherwise they should be considered private.
+
+ typedef HashMap<NPIdentifier, PropertyCallback*> PropertyList;
+ typedef HashMap<NPIdentifier, Callback*> MethodList;
+ // These maps associate names with property and method pointers to be
+ // exposed to JavaScript.
+ PropertyList m_properties;
+ MethodList m_methods;
+
+ // The callback gets invoked when a call is made to an nonexistent method.
+ OwnPtr<Callback> m_fallbackCallback;
+
+private:
+ // NPObject callbacks.
+ friend struct CppNPObject;
+ bool hasMethod(NPIdentifier) const;
+ bool invoke(NPIdentifier, const NPVariant* args, size_t argCount,
+ NPVariant* result);
+ bool hasProperty(NPIdentifier) const;
+ bool getProperty(NPIdentifier, NPVariant* result) const;
+ bool setProperty(NPIdentifier, const NPVariant*);
+
+ // A lazily-initialized CppVariant representing this class. We retain 1
+ // reference to this object, and it is released on deletion.
+ CppVariant m_selfVariant;
+
+ // True if our np_object has been bound to a WebFrame, in which case it must
+ // be unregistered with V8 when we delete it.
+ bool m_boundToFrame;
+};
+
+#endif // CppBoundClass_h
diff --git a/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp b/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp
new file mode 100644
index 0000000..9539907
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CppVariant.h"
+
+#include "public/WebBindings.h"
+#include <limits>
+#include <wtf/Assertions.h>
+#include <wtf/StringExtras.h>
+
+using namespace WebKit;
+using namespace std;
+
+CppVariant::CppVariant()
+{
+ type = NPVariantType_Null;
+}
+
+// Note that Set() performs a deep copy, which is necessary to safely
+// call FreeData() on the value in the destructor.
+CppVariant::CppVariant(const CppVariant& original)
+{
+ type = NPVariantType_Null;
+ set(original);
+}
+
+// See comment for copy constructor, above.
+CppVariant& CppVariant::operator=(const CppVariant& original)
+{
+ if (&original != this)
+ set(original);
+ return *this;
+}
+
+CppVariant::~CppVariant()
+{
+ freeData();
+}
+
+void CppVariant::freeData()
+{
+ WebBindings::releaseVariantValue(this);
+}
+
+bool CppVariant::isEqual(const CppVariant& other) const
+{
+ if (type != other.type)
+ return false;
+
+ switch (type) {
+ case NPVariantType_Bool:
+ return (value.boolValue == other.value.boolValue);
+ case NPVariantType_Int32:
+ return (value.intValue == other.value.intValue);
+ case NPVariantType_Double:
+ return (value.doubleValue == other.value.doubleValue);
+ case NPVariantType_String: {
+ const NPString *this_value = &value.stringValue;
+ const NPString *other_value = &other.value.stringValue;
+ uint32_t len = this_value->UTF8Length;
+ return len == other_value->UTF8Length
+ && !strncmp(this_value->UTF8Characters,
+ other_value->UTF8Characters, len);
+ }
+ case NPVariantType_Null:
+ case NPVariantType_Void:
+ return true;
+ case NPVariantType_Object: {
+ NPObject* thisValue = value.objectValue;
+ NPObject* otherValue = other.value.objectValue;
+ return thisValue->_class == otherValue->_class
+ && thisValue->referenceCount == otherValue->referenceCount;
+ }
+ }
+ return false;
+}
+
+void CppVariant::copyToNPVariant(NPVariant* result) const
+{
+ result->type = type;
+ switch (type) {
+ case NPVariantType_Bool:
+ result->value.boolValue = value.boolValue;
+ break;
+ case NPVariantType_Int32:
+ result->value.intValue = value.intValue;
+ break;
+ case NPVariantType_Double:
+ result->value.doubleValue = value.doubleValue;
+ break;
+ case NPVariantType_String:
+ WebBindings::initializeVariantWithStringCopy(result, &value.stringValue);
+ break;
+ case NPVariantType_Null:
+ case NPVariantType_Void:
+ // Nothing to set.
+ break;
+ case NPVariantType_Object:
+ result->type = NPVariantType_Object;
+ result->value.objectValue = WebBindings::retainObject(value.objectValue);
+ break;
+ }
+}
+
+void CppVariant::set(const NPVariant& newValue)
+{
+ freeData();
+ switch (newValue.type) {
+ case NPVariantType_Bool:
+ set(newValue.value.boolValue);
+ break;
+ case NPVariantType_Int32:
+ set(newValue.value.intValue);
+ break;
+ case NPVariantType_Double:
+ set(newValue.value.doubleValue);
+ break;
+ case NPVariantType_String:
+ set(newValue.value.stringValue);
+ break;
+ case NPVariantType_Null:
+ case NPVariantType_Void:
+ type = newValue.type;
+ break;
+ case NPVariantType_Object:
+ set(newValue.value.objectValue);
+ break;
+ }
+}
+
+void CppVariant::setNull()
+{
+ freeData();
+ type = NPVariantType_Null;
+}
+
+void CppVariant::set(bool newValue)
+{
+ freeData();
+ type = NPVariantType_Bool;
+ value.boolValue = newValue;
+}
+
+void CppVariant::set(int32_t newValue)
+{
+ freeData();
+ type = NPVariantType_Int32;
+ value.intValue = newValue;
+}
+
+void CppVariant::set(double newValue)
+{
+ freeData();
+ type = NPVariantType_Double;
+ value.doubleValue = newValue;
+}
+
+// The newValue must be a null-terminated string.
+void CppVariant::set(const char* newValue)
+{
+ freeData();
+ type = NPVariantType_String;
+ NPString newString = {newValue,
+ static_cast<uint32_t>(strlen(newValue))};
+ WebBindings::initializeVariantWithStringCopy(this, &newString);
+}
+
+void CppVariant::set(const string& newValue)
+{
+ freeData();
+ type = NPVariantType_String;
+ NPString newString = {newValue.data(),
+ static_cast<uint32_t>(newValue.size())};
+ WebBindings::initializeVariantWithStringCopy(this, &newString);
+}
+
+void CppVariant::set(const NPString& newValue)
+{
+ freeData();
+ type = NPVariantType_String;
+ WebBindings::initializeVariantWithStringCopy(this, &newValue);
+}
+
+void CppVariant::set(NPObject* newValue)
+{
+ freeData();
+ type = NPVariantType_Object;
+ value.objectValue = WebBindings::retainObject(newValue);
+}
+
+string CppVariant::toString() const
+{
+ ASSERT(isString());
+ return string(value.stringValue.UTF8Characters,
+ value.stringValue.UTF8Length);
+}
+
+int32_t CppVariant::toInt32() const
+{
+ if (isInt32())
+ return value.intValue;
+ if (isDouble())
+ return static_cast<int32_t>(value.doubleValue);
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+double CppVariant::toDouble() const
+{
+ if (isInt32())
+ return static_cast<double>(value.intValue);
+ if (isDouble())
+ return value.doubleValue;
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+bool CppVariant::toBoolean() const
+{
+ ASSERT(isBool());
+ return value.boolValue;
+}
+
+Vector<string> CppVariant::toStringVector() const
+{
+
+ ASSERT(isObject());
+ Vector<string> stringVector;
+ NPObject* npValue = value.objectValue;
+ NPIdentifier lengthId = WebBindings::getStringIdentifier("length");
+
+ if (!WebBindings::hasProperty(0, npValue, lengthId))
+ return stringVector;
+
+ NPVariant lengthValue;
+ if (!WebBindings::getProperty(0, npValue, lengthId, &lengthValue))
+ return stringVector;
+
+ int length = 0;
+ // The length is a double in some cases.
+ if (NPVARIANT_IS_DOUBLE(lengthValue))
+ length = static_cast<int>(NPVARIANT_TO_DOUBLE(lengthValue));
+ else if (NPVARIANT_IS_INT32(lengthValue))
+ length = NPVARIANT_TO_INT32(lengthValue);
+ WebBindings::releaseVariantValue(&lengthValue);
+
+ // For sanity, only allow 100 items.
+ length = min(100, length);
+ for (int i = 0; i < length; ++i) {
+ // Get each of the items.
+ char indexInChar[20]; // Enough size to store 32-bit integer
+ snprintf(indexInChar, 20, "%d", i);
+ string index(indexInChar);
+ NPIdentifier indexId = WebBindings::getStringIdentifier(index.c_str());
+ if (!WebBindings::hasProperty(0, npValue, indexId))
+ continue;
+ NPVariant indexValue;
+ if (!WebBindings::getProperty(0, npValue, indexId, &indexValue))
+ continue;
+ if (NPVARIANT_IS_STRING(indexValue)) {
+ string item(NPVARIANT_TO_STRING(indexValue).UTF8Characters,
+ NPVARIANT_TO_STRING(indexValue).UTF8Length);
+ stringVector.append(item);
+ }
+ WebBindings::releaseVariantValue(&indexValue);
+ }
+ return stringVector;
+}
+
+bool CppVariant::invoke(const string& method, const CppVariant* arguments,
+ uint32_t argumentCount, CppVariant& result) const
+{
+ ASSERT(isObject());
+ NPIdentifier methodName = WebBindings::getStringIdentifier(method.c_str());
+ NPObject* npObject = value.objectValue;
+ if (!WebBindings::hasMethod(0, npObject, methodName))
+ return false;
+ NPVariant r;
+ bool status = WebBindings::invoke(0, npObject, methodName, arguments, argumentCount, &r);
+ result.set(r);
+ return status;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/CppVariant.h b/WebKitTools/DumpRenderTree/chromium/CppVariant.h
new file mode 100644
index 0000000..d34a163
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/CppVariant.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ This file contains the declaration for CppVariant, a type used by C++ classes
+ that are to be bound to JavaScript objects.
+
+ CppVariant exists primarily as an interface between C++ callers and the
+ corresponding NPVariant type. CppVariant also provides a number of
+ convenience constructors and accessors, so that the NPVariantType values
+ don't need to be exposed, and a destructor to free any memory allocated for
+ string values.
+*/
+
+#ifndef CppVariant_h
+#define CppVariant_h
+
+#include "base/basictypes.h"
+#include "public/WebBindings.h"
+#include <string>
+#include <wtf/Vector.h>
+
+class CppVariant : public NPVariant {
+public:
+ CppVariant();
+ ~CppVariant();
+ void setNull();
+ void set(bool);
+ void set(int32_t);
+ void set(double);
+
+ // Note that setting a CppVariant to a string value involves copying the
+ // string data, which must be freed with a call to freeData() when the
+ // CppVariant is set to a different value or is no longer needed. Normally
+ // this is handled by the other set() methods and by the destructor.
+ void set(const char*); // Must be a null-terminated string.
+ void set(const std::string&);
+ void set(const NPString&);
+ void set(const NPVariant&);
+
+ // Note that setting a CppVariant to an NPObject involves ref-counting
+ // the actual object. freeData() should only be called if the CppVariant
+ // is no longer needed. The other set() methods handle this internally.
+ // Also, the object's NPClass is expected to be a static object: neither
+ // the NP runtime nor CppVariant will ever free it.
+ void set(NPObject*_value);
+
+ // These three methods all perform deep copies of any string data. This
+ // allows local CppVariants to be released by the destructor without
+ // corrupting their sources. In performance-critical code, or when strings
+ // are very long, avoid creating new CppVariants.
+ // In case of NPObject as the data, the copying involves ref-counting
+ // as opposed to deep-copying. The ref-counting ensures that sources don't
+ // get corrupted when the copies get destroyed.
+ void copyToNPVariant(NPVariant* result) const;
+ CppVariant& operator=(const CppVariant& original);
+ CppVariant(const CppVariant& original);
+
+ // Calls NPN_ReleaseVariantValue, which frees any string data
+ // held by the object and sets its type to null.
+ // In case of NPObject, the NPN_ReleaseVariantValue decrements
+ // the ref-count (releases when ref-count becomes 0)
+ void freeData();
+
+ // Compares this CppVariant's type and value to another's. They must be
+ // identical in both type and value to be considered equal. For string and
+ // object types, a deep comparison is performed; that is, the contents of the
+ // strings, or the classes and refcounts of the objects, must be the same,
+ // but they need not be the same pointers.
+ bool isEqual(const CppVariant&) const;
+
+ // The value of a CppVariant may be read directly from its NPVariant (but
+ // should only be set using one of the set() methods above). Although the
+ // type of a CppVariant is likewise public, it can be accessed through these
+ // functions rather than directly if a caller wishes to avoid dependence on
+ // the NPVariantType values.
+ bool isBool() const { return (type == NPVariantType_Bool); }
+ bool isInt32() const { return (type == NPVariantType_Int32); }
+ bool isDouble() const { return (type == NPVariantType_Double); }
+ bool isNumber() const { return (isInt32() || isDouble()); }
+ bool isString() const { return (type == NPVariantType_String); }
+ bool isVoid() const { return (type == NPVariantType_Void); }
+ bool isNull() const { return (type == NPVariantType_Null); }
+ bool isEmpty() const { return (isVoid() || isNull()); }
+ bool isObject() const { return (type == NPVariantType_Object); }
+
+ // Converters. The CppVariant must be of a type convertible to these values.
+ // For example, toInt32() works only if isNumber() is true.
+ std::string toString() const;
+ int32_t toInt32() const;
+ double toDouble() const;
+ bool toBoolean() const;
+ // Returns a vector of strings for the specified argument. This is useful
+ // for converting a JavaScript array of strings into a vector of strings.
+ Vector<std::string> toStringVector() const;
+
+ // Invoke method of the given name on an object with the supplied arguments.
+ // The first argument should be the object on which the method is to be
+ // invoked. Returns whether the method was successfully invoked. If the
+ // method was invoked successfully, any return value is stored in the
+ // CppVariant specified by result.
+ bool invoke(const std::string&, const CppVariant* arguments,
+ uint32_t argumentCount, CppVariant& result) const;
+};
+
+#endif // CppVariant_h
diff --git a/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp
new file mode 100644
index 0000000..c81480f
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "TestShell.h"
+#include "webkit/support/webkit_support.h"
+#include <wtf/Vector.h>
+#if OS(MAC_OS_X)
+#include "WebSystemInterface.h"
+#endif
+
+using namespace std;
+
+static const char optionComplexText[] = "--complex-text";
+static const char optionDumpAllPixels[] = "--dump-all-pixels";
+static const char optionNotree[] = "--notree";
+static const char optionPixelTests[] = "--pixel-tests";
+static const char optionThreaded[] = "--threaded";
+static const char optionTree[] = "--tree";
+
+static void runTest(TestShell& shell, TestParams& params, const string& testName)
+{
+ string pathOrURL = testName;
+ string::size_type separatorPosition = pathOrURL.find("'");
+ if (separatorPosition != string::npos) {
+ params.pixelHash = pathOrURL.substr(separatorPosition + 1);
+ pathOrURL.erase(separatorPosition);
+ }
+ params.testUrl = webkit_support::CreateURLForPathOrURL(pathOrURL);
+ shell.resetTestController();
+ shell.runFileTest(params);
+}
+
+int main(int argc, char* argv[])
+{
+#if OS(MAC_OS_X)
+ // Need to call before instantiate WebKitClient.
+ InitWebCoreSystemInterface();
+#endif
+ webkit_support::SetUpTestEnvironment();
+
+ TestParams params;
+ Vector<string> tests;
+ bool serverMode = false;
+ for (int i = 1; i < argc; ++i) {
+ string argument(argv[i]);
+ if (argument == "-")
+ serverMode = true;
+ else if (argument == optionNotree)
+ params.dumpTree = false;
+ else if (argument == optionPixelTests)
+ params.dumpPixels = true;
+ else if (argument.size() && argument[0] == '-')
+ fprintf(stderr, "Unknown option: %s\n", argv[i]);
+ else
+ tests.append(argument);
+ }
+
+ { // Explicit scope for the TestShell instance.
+ TestShell shell;
+ if (serverMode && !tests.size()) {
+ params.printSeparators = true;
+ char testString[2048]; // 2048 is the same as the sizes of other platforms.
+ while (fgets(testString, sizeof(testString), stdin)) {
+ char* newLinePosition = strchr(testString, '\n');
+ if (newLinePosition)
+ *newLinePosition = '\0';
+ if (testString[0] == '\0')
+ continue;
+ runTest(shell, params, testString);
+ }
+ } else if (!tests.size())
+ printf("#EOF\n");
+ else {
+ params.printSeparators = tests.size() > 1;
+ for (unsigned i = 0; i < tests.size(); i++)
+ runTest(shell, params, tests[i]);
+ }
+
+ shell.callJSGC();
+ shell.callJSGC();
+
+ // When we finish the last test, cleanup the LayoutTestController.
+ // It may have references to not-yet-cleaned up windows. By
+ // cleaning up here we help purify reports.
+ shell.resetTestController();
+ }
+
+ webkit_support::TearDownTestEnvironment();
+ return EXIT_SUCCESS;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.cpp b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp
new file mode 100644
index 0000000..c48aaf4
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file contains the definition for EventSender.
+//
+// Some notes about drag and drop handling:
+// Windows drag and drop goes through a system call to doDragDrop. At that
+// point, program control is given to Windows which then periodically makes
+// callbacks into the webview. This won't work for layout tests, so instead,
+// we queue up all the mouse move and mouse up events. When the test tries to
+// start a drag (by calling EvenSendingController::doDragDrop), we take the
+// events in the queue and replay them.
+// The behavior of queuing events and replaying them can be disabled by a
+// layout test by setting eventSender.dragMode to false.
+
+#include "config.h"
+#include "EventSender.h"
+
+#include "TestShell.h"
+#include "base/keyboard_codes.h"
+#include "base/time.h"
+#include "public/WebDragData.h"
+#include "public/WebDragOperation.h"
+#include "public/WebPoint.h"
+#include "public/WebString.h"
+#include "public/WebView.h"
+#include "webkit/support/webkit_support.h"
+#include <wtf/Deque.h>
+#include <wtf/StringExtras.h>
+
+#if OS(WINDOWS)
+#include "public/win/WebInputEventFactory.h"
+#endif
+
+// FIXME: layout before each event?
+
+using namespace base;
+using namespace std;
+using namespace WebKit;
+
+TestShell* EventSender::testShell = 0;
+WebPoint EventSender::lastMousePos;
+WebMouseEvent::Button EventSender::pressedButton = WebMouseEvent::ButtonNone;
+WebMouseEvent::Button EventSender::lastButtonType = WebMouseEvent::ButtonNone;
+
+struct SavedEvent {
+ enum SavedEventType {
+ Unspecified,
+ MouseUp,
+ MouseMove,
+ LeapForward
+ };
+
+ SavedEventType type;
+ WebMouseEvent::Button buttonType; // For MouseUp.
+ WebPoint pos; // For MouseMove.
+ int milliseconds; // For LeapForward.
+
+ SavedEvent()
+ : type(Unspecified)
+ , buttonType(WebMouseEvent::ButtonNone)
+ , milliseconds(0) {}
+};
+
+static WebDragData currentDragData;
+static WebDragOperation currentDragEffect;
+static WebDragOperationsMask currentDragEffectsAllowed;
+static bool replayingSavedEvents = false;
+static Deque<SavedEvent> mouseEventQueue;
+
+// Time and place of the last mouse up event.
+static double lastClickTimeSec = 0;
+static WebPoint lastClickPos;
+static int clickCount = 0;
+
+// maximum distance (in space and time) for a mouse click
+// to register as a double or triple click
+static const double multipleClickTimeSec = 1;
+static const int multipleClickRadiusPixels = 5;
+
+// How much we should scroll per event - the value here is chosen to
+// match the WebKit impl and layout test results.
+static const float scrollbarPixelsPerTick = 40.0f;
+
+inline bool outsideMultiClickRadius(const WebPoint& a, const WebPoint& b)
+{
+ return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) >
+ multipleClickRadiusPixels * multipleClickRadiusPixels;
+}
+
+// Used to offset the time the event hander things an event happened. This is
+// done so tests can run without a delay, but bypass checks that are time
+// dependent (e.g., dragging has a timeout vs selection).
+static uint32 timeOffsetMs = 0;
+
+static double getCurrentEventTimeSec()
+{
+ return (TimeTicks::Now().ToInternalValue() / Time::kMicrosecondsPerMillisecond + timeOffsetMs) / 1000.0;
+}
+
+static void advanceEventTime(int32_t deltaMs)
+{
+ timeOffsetMs += deltaMs;
+}
+
+static void initMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b,
+ const gfx::Point& pos, WebMouseEvent* e)
+{
+ e->type = t;
+ e->button = b;
+ e->modifiers = 0;
+ e->x = pos.x();
+ e->y = pos.y();
+ e->globalX = pos.x();
+ e->globalY = pos.y();
+ e->timeStampSeconds = getCurrentEventTimeSec();
+ e->clickCount = clickCount;
+}
+
+// Returns true if the specified key is the system key.
+static bool applyKeyModifier(const string& modifierName, WebInputEvent* event)
+{
+ bool isSystemKey = false;
+ const char* characters = modifierName.c_str();
+ if (!strcmp(characters, "ctrlKey")
+#if !OS(MAC_OS_X)
+ || !strcmp(characters, "addSelectionKey")
+#endif
+ ) {
+ event->modifiers |= WebInputEvent::ControlKey;
+ } else if (!strcmp(characters, "shiftKey") || !strcmp(characters, "rangeSelectionKey"))
+ event->modifiers |= WebInputEvent::ShiftKey;
+ else if (!strcmp(characters, "altKey")) {
+ event->modifiers |= WebInputEvent::AltKey;
+#if !OS(MAC_OS_X)
+ // On Windows all keys with Alt modifier will be marked as system key.
+ // We keep the same behavior on Linux, see:
+ // WebKit/chromium/src/gtk/WebInputEventFactory.cpp
+ // If we want to change this behavior on Linux, this piece of code must be
+ // kept in sync with the related code in above file.
+ isSystemKey = true;
+#endif
+#if OS(MAC_OS_X)
+ } else if (!strcmp(characters, "metaKey") || !strcmp(characters, "addSelectionKey")) {
+ event->modifiers |= WebInputEvent::MetaKey;
+ // On Mac only command key presses are marked as system key.
+ // See the related code in: WebKit/chromium/src/mac/WebInputEventFactory.cpp
+ // It must be kept in sync with the related code in above file.
+ isSystemKey = true;
+#else
+ } else if (!strcmp(characters, "metaKey")) {
+ event->modifiers |= WebInputEvent::MetaKey;
+#endif
+ }
+ return isSystemKey;
+}
+
+static bool applyKeyModifiers(const CppVariant* argument, WebInputEvent* event)
+{
+ bool isSystemKey = false;
+ if (argument->isObject()) {
+ Vector<string> modifiers = argument->toStringVector();
+ for (Vector<string>::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i)
+ isSystemKey |= applyKeyModifier(*i, event);
+ } else if (argument->isString())
+ isSystemKey = applyKeyModifier(argument->toString(), event);
+ return isSystemKey;
+}
+
+// Get the edit command corresponding to a keyboard event.
+// Returns true if the specified event corresponds to an edit command, the name
+// of the edit command will be stored in |*name|.
+bool getEditCommand(const WebKeyboardEvent& event, string* name)
+{
+#if OS(MAC_OS_X)
+ // We only cares about Left,Right,Up,Down keys with Command or Command+Shift
+ // modifiers. These key events correspond to some special movement and
+ // selection editor commands, and was supposed to be handled in
+ // WebKit/chromium/src/EditorClientImpl.cpp. But these keys will be marked
+ // as system key, which prevents them from being handled. Thus they must be
+ // handled specially.
+ if ((event.modifiers & ~WebKeyboardEvent::ShiftKey) != WebKeyboardEvent::MetaKey)
+ return false;
+
+ switch (event.windowsKeyCode) {
+ case base::VKEY_LEFT:
+ *name = "MoveToBeginningOfLine";
+ break;
+ case base::VKEY_RIGHT:
+ *name = "MoveToEndOfLine";
+ break;
+ case base::VKEY_UP:
+ *name = "MoveToBeginningOfDocument";
+ break;
+ case base::VKEY_DOWN:
+ *name = "MoveToEndOfDocument";
+ break;
+ default:
+ return false;
+ }
+
+ if (event.modifiers & WebKeyboardEvent::ShiftKey)
+ name->append("AndModifySelection");
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Key event location code introduced in DOM Level 3.
+// See also: http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents
+enum KeyLocationCode {
+ DOMKeyLocationStandard = 0x00,
+ DOMKeyLocationLeft = 0x01,
+ DOMKeyLocationRight = 0x02,
+ DOMKeyLocationNumpad = 0x03
+};
+
+EventSender::EventSender(TestShell* shell)
+ : m_methodFactory(this)
+{
+ // Set static testShell variable since we can't do it in an initializer list.
+ // We also need to be careful not to assign testShell to new windows which are
+ // temporary.
+ if (!testShell)
+ testShell = shell;
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling bindToJavaScript() (defined
+ // by CppBoundClass, the parent to EventSender).
+ bindMethod("mouseDown", &EventSender::mouseDown);
+ bindMethod("mouseUp", &EventSender::mouseUp);
+ bindMethod("contextClick", &EventSender::contextClick);
+ bindMethod("mouseMoveTo", &EventSender::mouseMoveTo);
+ bindMethod("mouseWheelTo", &EventSender::mouseWheelTo);
+ bindMethod("leapForward", &EventSender::leapForward);
+ bindMethod("keyDown", &EventSender::keyDown);
+ bindMethod("dispatchMessage", &EventSender::dispatchMessage);
+ bindMethod("enableDOMUIEventLogging", &EventSender::enableDOMUIEventLogging);
+ bindMethod("fireKeyboardEventsToElement", &EventSender::fireKeyboardEventsToElement);
+ bindMethod("clearKillRing", &EventSender::clearKillRing);
+ bindMethod("textZoomIn", &EventSender::textZoomIn);
+ bindMethod("textZoomOut", &EventSender::textZoomOut);
+ bindMethod("zoomPageIn", &EventSender::zoomPageIn);
+ bindMethod("zoomPageOut", &EventSender::zoomPageOut);
+ bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick);
+ bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles);
+
+ // When set to true (the default value), we batch mouse move and mouse up
+ // events so we can simulate drag & drop.
+ bindProperty("dragMode", &dragMode);
+#if OS(WINDOWS)
+ bindProperty("WM_KEYDOWN", &wmKeyDown);
+ bindProperty("WM_KEYUP", &wmKeyUp);
+ bindProperty("WM_CHAR", &wmChar);
+ bindProperty("WM_DEADCHAR", &wmDeadChar);
+ bindProperty("WM_SYSKEYDOWN", &wmSysKeyDown);
+ bindProperty("WM_SYSKEYUP", &wmSysKeyUp);
+ bindProperty("WM_SYSCHAR", &wmSysChar);
+ bindProperty("WM_SYSDEADCHAR", &wmSysDeadChar);
+#endif
+}
+
+void EventSender::reset()
+{
+ // The test should have finished a drag and the mouse button state.
+ ASSERT(currentDragData.isNull());
+ currentDragData.reset();
+ currentDragEffect = WebKit::WebDragOperationNone;
+ currentDragEffectsAllowed = WebKit::WebDragOperationNone;
+ pressedButton = WebMouseEvent::ButtonNone;
+ dragMode.set(true);
+#if OS(WINDOWS)
+ wmKeyDown.set(WM_KEYDOWN);
+ wmKeyUp.set(WM_KEYUP);
+ wmChar.set(WM_CHAR);
+ wmDeadChar.set(WM_DEADCHAR);
+ wmSysKeyDown.set(WM_SYSKEYDOWN);
+ wmSysKeyUp.set(WM_SYSKEYUP);
+ wmSysChar.set(WM_SYSCHAR);
+ wmSysDeadChar.set(WM_SYSDEADCHAR);
+#endif
+ lastMousePos = WebPoint(0, 0);
+ lastClickTimeSec = 0;
+ lastClickPos = WebPoint(0, 0);
+ clickCount = 0;
+ lastButtonType = WebMouseEvent::ButtonNone;
+ timeOffsetMs = 0;
+}
+
+WebView* EventSender::webview()
+{
+ return testShell->webView();
+}
+
+void EventSender::doDragDrop(const WebKit::WebPoint& eventPos,
+ const WebDragData& dragData,
+ WebDragOperationsMask mask)
+{
+ WebMouseEvent event;
+ initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event);
+ WebPoint clientPoint(event.x, event.y);
+ WebPoint screenPoint(event.globalX, event.globalY);
+ currentDragData = dragData;
+ currentDragEffectsAllowed = mask;
+ currentDragEffect = webview()->dragTargetDragEnter(dragData, 0, clientPoint, screenPoint, currentDragEffectsAllowed);
+
+ // Finish processing events.
+ replaySavedEvents();
+}
+
+WebMouseEvent::Button EventSender::getButtonTypeFromButtonNumber(int buttonCode)
+{
+ if (!buttonCode)
+ return WebMouseEvent::ButtonLeft;
+ if (buttonCode == 2)
+ return WebMouseEvent::ButtonRight;
+ return WebMouseEvent::ButtonMiddle;
+}
+
+int EventSender::getButtonNumberFromSingleArg(const CppArgumentList& arguments)
+{
+ int buttonCode = 0;
+ if (arguments.size() > 0 && arguments[0].isNumber())
+ buttonCode = arguments[0].toInt32();
+ return buttonCode;
+}
+
+void EventSender::updateClickCountForButton(WebMouseEvent::Button buttonType)
+{
+ if ((getCurrentEventTimeSec() - lastClickTimeSec < multipleClickTimeSec)
+ && (!outsideMultiClickRadius(lastMousePos, lastClickPos))
+ && (buttonType == lastButtonType))
+ ++clickCount;
+ else {
+ clickCount = 1;
+ lastButtonType = buttonType;
+ }
+}
+
+//
+// Implemented javascript methods.
+//
+
+void EventSender::mouseDown(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (result) // Could be 0 if invoked asynchronously.
+ result->setNull();
+
+ webview()->layout();
+
+ int buttonNumber = getButtonNumberFromSingleArg(arguments);
+ ASSERT(buttonNumber != -1);
+
+ WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber);
+
+ updateClickCountForButton(buttonType);
+
+ WebMouseEvent event;
+ pressedButton = buttonType;
+ initMouseEvent(WebInputEvent::MouseDown, buttonType, lastMousePos, &event);
+ if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString()))
+ applyKeyModifiers(&(arguments[1]), &event);
+ webview()->handleInputEvent(event);
+}
+
+void EventSender::mouseUp(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (result) // Could be 0 if invoked asynchronously.
+ result->setNull();
+
+ webview()->layout();
+
+ int buttonNumber = getButtonNumberFromSingleArg(arguments);
+ ASSERT(buttonNumber != -1);
+
+ WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber);
+
+ if (isDragMode() && !replayingSavedEvents) {
+ SavedEvent savedEvent;
+ savedEvent.type = SavedEvent::MouseUp;
+ savedEvent.buttonType = buttonType;
+ mouseEventQueue.append(savedEvent);
+ replaySavedEvents();
+ } else {
+ WebMouseEvent event;
+ initMouseEvent(WebInputEvent::MouseUp, buttonType, lastMousePos, &event);
+ if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString()))
+ applyKeyModifiers(&(arguments[1]), &event);
+ doMouseUp(event);
+ }
+}
+
+void EventSender::doMouseUp(const WebMouseEvent& e)
+{
+ webview()->handleInputEvent(e);
+
+ pressedButton = WebMouseEvent::ButtonNone;
+ lastClickTimeSec = e.timeStampSeconds;
+ lastClickPos = lastMousePos;
+
+ // If we're in a drag operation, complete it.
+ if (currentDragData.isNull())
+ return;
+ WebPoint clientPoint(e.x, e.y);
+ WebPoint screenPoint(e.globalX, e.globalY);
+
+ currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed);
+ if (currentDragEffect)
+ webview()->dragTargetDrop(clientPoint, screenPoint);
+ else
+ webview()->dragTargetDragLeave();
+ webview()->dragSourceEndedAt(clientPoint, screenPoint, currentDragEffect);
+ webview()->dragSourceSystemDragEnded();
+
+ currentDragData.reset();
+}
+
+void EventSender::mouseMoveTo(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber())
+ return;
+ webview()->layout();
+
+ WebPoint mousePos(arguments[0].toInt32(), arguments[1].toInt32());
+
+ if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) {
+ SavedEvent savedEvent;
+ savedEvent.type = SavedEvent::MouseMove;
+ savedEvent.pos = mousePos;
+ mouseEventQueue.append(savedEvent);
+ } else {
+ WebMouseEvent event;
+ initMouseEvent(WebInputEvent::MouseMove, pressedButton, mousePos, &event);
+ doMouseMove(event);
+ }
+}
+
+void EventSender::mouseWheelTo(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber())
+ return;
+
+ // Force a layout here just to make sure every position has been
+ // determined before we send events (as well as all the other methods
+ // that send an event do). The layout test calling this
+ // (scrollbars/overflow-scrollbar-horizontal-wheel-scroll.html, only one
+ // for now) does not rely on this though.
+ webview()->layout();
+
+ int horizontal = arguments[0].toInt32();
+ int vertical = arguments[1].toInt32();
+
+ WebMouseWheelEvent event;
+ initMouseEvent(WebInputEvent::MouseWheel, pressedButton, lastMousePos, &event);
+ event.wheelTicksX = static_cast<float>(horizontal);
+ event.wheelTicksY = static_cast<float>(vertical);
+ event.deltaX = -horizontal * scrollbarPixelsPerTick;
+ event.deltaY = -vertical * scrollbarPixelsPerTick;
+ webview()->handleInputEvent(event);
+}
+
+void EventSender::doMouseMove(const WebMouseEvent& e)
+{
+ lastMousePos = WebPoint(e.x, e.y);
+
+ webview()->handleInputEvent(e);
+
+ if (pressedButton == WebMouseEvent::ButtonNone || currentDragData.isNull())
+ return;
+ WebPoint clientPoint(e.x, e.y);
+ WebPoint screenPoint(e.globalX, e.globalY);
+ currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed);
+}
+
+void EventSender::keyDown(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() < 1 || !arguments[0].isString())
+ return;
+ bool generateChar = false;
+
+ // FIXME: I'm not exactly sure how we should convert the string to a key
+ // event. This seems to work in the cases I tested.
+ // FIXME: Should we also generate a KEY_UP?
+ string codeStr = arguments[0].toString();
+
+ // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when
+ // Windows uses \r for "Enter".
+ int code = 0;
+ int text = 0;
+ bool needsShiftKeyModifier = false;
+ if ("\n" == codeStr) {
+ generateChar = true;
+ text = code = base::VKEY_RETURN;
+ } else if ("rightArrow" == codeStr)
+ code = base::VKEY_RIGHT;
+ else if ("downArrow" == codeStr)
+ code = base::VKEY_DOWN;
+ else if ("leftArrow" == codeStr)
+ code = base::VKEY_LEFT;
+ else if ("upArrow" == codeStr)
+ code = base::VKEY_UP;
+ else if ("delete" == codeStr)
+ code = base::VKEY_BACK;
+ else if ("pageUp" == codeStr)
+ code = base::VKEY_PRIOR;
+ else if ("pageDown" == codeStr)
+ code = base::VKEY_NEXT;
+ else if ("home" == codeStr)
+ code = base::VKEY_HOME;
+ else if ("end" == codeStr)
+ code = base::VKEY_END;
+ else {
+ // Compare the input string with the function-key names defined by the
+ // DOM spec (i.e. "F1",...,"F24"). If the input string is a function-key
+ // name, set its key code.
+ for (int i = 1; i <= 24; ++i) {
+ char functionChars[10];
+ snprintf(functionChars, 10, "F%d", i);
+ string functionKeyName(functionChars);
+ if (functionKeyName == codeStr) {
+ code = base::VKEY_F1 + (i - 1);
+ break;
+ }
+ }
+ if (!code) {
+ WebString webCodeStr = WebString::fromUTF8(codeStr.data(), codeStr.size());
+ ASSERT(webCodeStr.length() == 1);
+ text = code = webCodeStr.data()[0];
+ needsShiftKeyModifier = needsShiftModifier(code);
+ if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z')
+ code -= 'a' - 'A';
+ generateChar = true;
+ }
+ }
+
+ // For one generated keyboard event, we need to generate a keyDown/keyUp
+ // pair; refer to EventSender.cpp in WebKit/WebKitTools/DumpRenderTree/win.
+ // On Windows, we might also need to generate a char event to mimic the
+ // Windows event flow; on other platforms we create a merged event and test
+ // the event flow that that platform provides.
+ WebKeyboardEvent eventDown, eventChar, eventUp;
+ eventDown.type = WebInputEvent::RawKeyDown;
+ eventDown.modifiers = 0;
+ eventDown.windowsKeyCode = code;
+ if (generateChar) {
+ eventDown.text[0] = text;
+ eventDown.unmodifiedText[0] = text;
+ }
+ eventDown.setKeyIdentifierFromWindowsKeyCode();
+
+ if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString()))
+ eventDown.isSystemKey = applyKeyModifiers(&(arguments[1]), &eventDown);
+
+ if (needsShiftKeyModifier)
+ eventDown.modifiers |= WebInputEvent::ShiftKey;
+
+ // See if KeyLocation argument is given.
+ if (arguments.size() >= 3 && arguments[2].isNumber()) {
+ int location = arguments[2].toInt32();
+ if (location == DOMKeyLocationNumpad)
+ eventDown.modifiers |= WebInputEvent::IsKeyPad;
+ }
+
+ eventChar = eventUp = eventDown;
+ eventUp.type = WebInputEvent::KeyUp;
+ // EventSender.m forces a layout here, with at least one
+ // test (fast/forms/focus-control-to-page.html) relying on this.
+ webview()->layout();
+
+ // In the browser, if a keyboard event corresponds to an editor command,
+ // the command will be dispatched to the renderer just before dispatching
+ // the keyboard event, and then it will be executed in the
+ // RenderView::handleCurrentKeyboardEvent() method, which is called from
+ // third_party/WebKit/WebKit/chromium/src/EditorClientImpl.cpp.
+ // We just simulate the same behavior here.
+ string editCommand;
+ if (getEditCommand(eventDown, &editCommand))
+ testShell->webViewHost()->setEditCommand(editCommand, "");
+
+ webview()->handleInputEvent(eventDown);
+
+ testShell->webViewHost()->clearEditCommand();
+
+ if (generateChar) {
+ eventChar.type = WebInputEvent::Char;
+ eventChar.keyIdentifier[0] = '\0';
+ webview()->handleInputEvent(eventChar);
+ }
+
+ webview()->handleInputEvent(eventUp);
+}
+
+void EventSender::dispatchMessage(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+#if OS(WINDOWS)
+ if (arguments.size() == 3) {
+ // Grab the message id to see if we need to dispatch it.
+ int msg = arguments[0].toInt32();
+
+ // WebKit's version of this function stuffs a MSG struct and uses
+ // TranslateMessage and DispatchMessage. We use a WebKeyboardEvent, which
+ // doesn't need to receive the DeadChar and SysDeadChar messages.
+ if (msg == WM_DEADCHAR || msg == WM_SYSDEADCHAR)
+ return;
+
+ webview()->layout();
+
+ unsigned long lparam = static_cast<unsigned long>(arguments[2].toDouble());
+ webview()->handleInputEvent(WebInputEventFactory::keyboardEvent(0, msg, arguments[1].toInt32(), lparam));
+ } else
+ ASSERT_NOT_REACHED();
+#endif
+}
+
+bool EventSender::needsShiftModifier(int keyCode)
+{
+ // If code is an uppercase letter, assign a SHIFT key to
+ // eventDown.modifier, this logic comes from
+ // WebKit/WebKitTools/DumpRenderTree/Win/EventSender.cpp
+ return (keyCode & 0xFF) >= 'A' && (keyCode & 0xFF) <= 'Z';
+}
+
+void EventSender::leapForward(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ if (arguments.size() < 1 || !arguments[0].isNumber())
+ return;
+
+ int milliseconds = arguments[0].toInt32();
+ if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) {
+ SavedEvent savedEvent;
+ savedEvent.type = SavedEvent::LeapForward;
+ savedEvent.milliseconds = milliseconds;
+ mouseEventQueue.append(savedEvent);
+ } else
+ doLeapForward(milliseconds);
+}
+
+void EventSender::doLeapForward(int milliseconds)
+{
+ advanceEventTime(milliseconds);
+}
+
+// Apple's port of WebKit zooms by a factor of 1.2 (see
+// WebKit/WebView/WebView.mm)
+void EventSender::textZoomIn(const CppArgumentList&, CppVariant* result)
+{
+ webview()->setZoomLevel(true, webview()->zoomLevel() + 1);
+ result->setNull();
+}
+
+void EventSender::textZoomOut(const CppArgumentList&, CppVariant* result)
+{
+ webview()->setZoomLevel(true, webview()->zoomLevel() - 1);
+ result->setNull();
+}
+
+void EventSender::zoomPageIn(const CppArgumentList&, CppVariant* result)
+{
+ webview()->setZoomLevel(false, webview()->zoomLevel() + 1);
+ result->setNull();
+}
+
+void EventSender::zoomPageOut(const CppArgumentList&, CppVariant* result)
+{
+ webview()->setZoomLevel(false, webview()->zoomLevel() - 1);
+ result->setNull();
+}
+
+void EventSender::replaySavedEvents()
+{
+ replayingSavedEvents = true;
+ while (!mouseEventQueue.isEmpty()) {
+ SavedEvent e = mouseEventQueue.first();
+ mouseEventQueue.removeFirst();
+
+ switch (e.type) {
+ case SavedEvent::MouseMove: {
+ WebMouseEvent event;
+ initMouseEvent(WebInputEvent::MouseMove, pressedButton, e.pos, &event);
+ doMouseMove(event);
+ break;
+ }
+ case SavedEvent::LeapForward:
+ doLeapForward(e.milliseconds);
+ break;
+ case SavedEvent::MouseUp: {
+ WebMouseEvent event;
+ initMouseEvent(WebInputEvent::MouseUp, e.buttonType, lastMousePos, &event);
+ doMouseUp(event);
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ replayingSavedEvents = false;
+}
+
+void EventSender::contextClick(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ webview()->layout();
+
+ updateClickCountForButton(WebMouseEvent::ButtonRight);
+
+ // Generate right mouse down and up.
+
+ WebMouseEvent event;
+ pressedButton = WebMouseEvent::ButtonRight;
+ initMouseEvent(WebInputEvent::MouseDown, WebMouseEvent::ButtonRight, lastMousePos, &event);
+ webview()->handleInputEvent(event);
+
+ initMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, lastMousePos, &event);
+ webview()->handleInputEvent(event);
+
+ pressedButton = WebMouseEvent::ButtonNone;
+}
+
+void EventSender::scheduleAsynchronousClick(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ webkit_support::PostTaskFromHere(m_methodFactory.NewRunnableMethod(
+ &EventSender::mouseDown, arguments, static_cast<CppVariant*>(0)));
+ webkit_support::PostTaskFromHere(m_methodFactory.NewRunnableMethod(
+ &EventSender::mouseUp, arguments, static_cast<CppVariant*>(0)));
+}
+
+void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVariant* result)
+{
+ currentDragData.initialize();
+ Vector<string> files = arguments[0].toStringVector();
+ for (size_t i = 0; i < files.size(); ++i)
+ currentDragData.appendToFileNames(webkit_support::GetAbsoluteWebStringFromUTF8Path(files[i]));
+ currentDragEffectsAllowed = WebKit::WebDragOperationCopy;
+
+ // Provide a drag source.
+ webview()->dragTargetDragEnter(currentDragData, 0, lastMousePos, lastMousePos, currentDragEffectsAllowed);
+
+ // dragMode saves events and then replays them later. We don't need/want that.
+ dragMode.set(false);
+
+ // Make the rest of eventSender think a drag is in progress.
+ pressedButton = WebMouseEvent::ButtonLeft;
+
+ result->setNull();
+}
+
+//
+// Unimplemented stubs
+//
+
+void EventSender::enableDOMUIEventLogging(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void EventSender::fireKeyboardEventsToElement(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
+
+void EventSender::clearKillRing(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.h b/WebKitTools/DumpRenderTree/chromium/EventSender.h
new file mode 100644
index 0000000..756b008
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/EventSender.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/*
+ EventSender class:
+ Bound to a JavaScript window.eventSender object using
+ CppBoundClass::bindToJavascript(), this allows layout tests to fire DOM events.
+*/
+
+#ifndef EventSender_h
+#define EventSender_h
+
+#include "CppBoundClass.h"
+#include "base/task.h"
+#include "public/WebDragOperation.h"
+#include "public/WebInputEvent.h"
+#include "public/WebPoint.h"
+
+class TestShell;
+
+namespace WebKit {
+class WebDragData;
+class WebView;
+}
+
+class EventSender : public CppBoundClass {
+public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ EventSender(TestShell*);
+
+ // Resets some static variable state.
+ void reset();
+
+ // Simulate drag&drop system call.
+ static void doDragDrop(const WebKit::WebPoint&,
+ const WebKit::WebDragData&,
+ WebKit::WebDragOperationsMask);
+
+ // JS callback methods.
+ void mouseDown(const CppArgumentList&, CppVariant*);
+ void mouseUp(const CppArgumentList&, CppVariant*);
+ void mouseMoveTo(const CppArgumentList&, CppVariant*);
+ void mouseWheelTo(const CppArgumentList&, CppVariant*);
+ void leapForward(const CppArgumentList&, CppVariant*);
+ void keyDown(const CppArgumentList&, CppVariant*);
+ void dispatchMessage(const CppArgumentList&, CppVariant*);
+ void textZoomIn(const CppArgumentList&, CppVariant*);
+ void textZoomOut(const CppArgumentList&, CppVariant*);
+ void zoomPageIn(const CppArgumentList&, CppVariant*);
+ void zoomPageOut(const CppArgumentList&, CppVariant*);
+ void scheduleAsynchronousClick(const CppArgumentList&, CppVariant*);
+ void beginDragWithFiles(const CppArgumentList&, CppVariant*);
+ CppVariant dragMode;
+
+ // Unimplemented stubs
+ void contextClick(const CppArgumentList&, CppVariant*);
+ void enableDOMUIEventLogging(const CppArgumentList&, CppVariant*);
+ void fireKeyboardEventsToElement(const CppArgumentList&, CppVariant*);
+ void clearKillRing(const CppArgumentList&, CppVariant*);
+
+ // Properties used in layout tests.
+#if defined(OS_WIN)
+ CppVariant wmKeyDown;
+ CppVariant wmKeyUp;
+ CppVariant wmChar;
+ CppVariant wmDeadChar;
+ CppVariant wmSysKeyDown;
+ CppVariant wmSysKeyUp;
+ CppVariant wmSysChar;
+ CppVariant wmSysDeadChar;
+#endif
+
+private:
+ // Returns the test shell's webview.
+ static WebKit::WebView* webview();
+
+ // Returns true if dragMode is true.
+ bool isDragMode() { return dragMode.isBool() && dragMode.toBoolean(); }
+
+ // Sometimes we queue up mouse move and mouse up events for drag drop
+ // handling purposes. These methods dispatch the event.
+ static void doMouseMove(const WebKit::WebMouseEvent&);
+ static void doMouseUp(const WebKit::WebMouseEvent&);
+ static void doLeapForward(int milliseconds);
+ static void replaySavedEvents();
+
+ // Helper to return the button type given a button code
+ static WebKit::WebMouseEvent::Button getButtonTypeFromButtonNumber(int);
+
+ // Helper to extract the button number from the optional argument in
+ // mouseDown and mouseUp
+ static int getButtonNumberFromSingleArg(const CppArgumentList&);
+
+ // Returns true if the specified key code passed in needs a shift key
+ // modifier to be passed into the generated event.
+ bool needsShiftModifier(int);
+
+ void updateClickCountForButton(WebKit::WebMouseEvent::Button);
+
+ ScopedRunnableMethodFactory<EventSender> m_methodFactory;
+
+ // Non-owning pointer. The EventSender is owned by the TestShell.
+ static TestShell* testShell;
+
+ // Location of last mouseMoveTo event.
+ static WebKit::WebPoint lastMousePos;
+
+ // Currently pressed mouse button (Left/Right/Middle or None)
+ static WebKit::WebMouseEvent::Button pressedButton;
+
+ // The last button number passed to mouseDown and mouseUp.
+ // Used to determine whether the click count continues to
+ // increment or not.
+ static WebKit::WebMouseEvent::Button lastButtonType;
+};
+
+#endif // EventSender_h
diff --git a/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp b/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp
new file mode 100644
index 0000000..81d5f62
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file input format is based loosely on
+// WebKitTools/DumpRenderTree/ImageDiff.m
+
+// The exact format of this tool's output to stdout is important, to match
+// what the run-webkit-tests script expects.
+
+#include "config.h"
+
+#include "gfx/codec/png_codec.h"
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <wtf/OwnArrayPtr.h>
+#include <wtf/Vector.h>
+
+using namespace gfx;
+using namespace std;
+
+// Causes the app to remain open, waiting for pairs of filenames on stdin.
+// The caller is then responsible for terminating this app.
+static const char optionPollStdin[] = "--use-stdin";
+static const char optionGenerateDiff[] = "--diff";
+
+// Return codes used by this utility.
+static const int statusSame = 0;
+static const int statusDifferent = 1;
+static const int statusError = 2;
+
+// Color codes.
+static const uint32_t rgbaRed = 0x000000ff;
+static const uint32_t rgbaAlpha = 0xff000000;
+
+class Image {
+public:
+ Image()
+ : m_width(0)
+ , m_height(0) {}
+
+ Image(const Image& image)
+ : m_width(image.m_width)
+ , m_height(image.m_height)
+ , m_data(image.m_data) {}
+
+ bool hasImage() const { return m_width > 0 && m_height > 0; }
+ int width() const { return m_width; }
+ int height() const { return m_height; }
+ const unsigned char* data() const { return &m_data.front(); }
+
+ // Creates the image from stdin with the given data length. On success, it
+ // will return true. On failure, no other methods should be accessed.
+ bool craeteFromStdin(size_t byteLength)
+ {
+ if (!byteLength)
+ return false;
+
+ OwnArrayPtr<unsigned char> source(new unsigned char[byteLength]);
+ if (fread(source.get(), 1, byteLength, stdin) != byteLength)
+ return false;
+
+ if (!PNGCodec::Decode(source.get(), byteLength, PNGCodec::FORMAT_RGBA,
+ &m_data, &m_width, &m_height)) {
+ clear();
+ return false;
+ }
+ return true;
+ }
+
+ // Creates the image from the given filename on disk, and returns true on
+ // success.
+ bool createFromFilename(const char* filename)
+ {
+ FILE* f = fopen(filename, "rb");
+ if (!f)
+ return false;
+
+ vector<unsigned char> compressed;
+ const int bufSize = 1024;
+ unsigned char buf[bufSize];
+ size_t numRead = 0;
+ while ((numRead = fread(buf, 1, bufSize, f)) > 0)
+ std::copy(buf, &buf[numRead], std::back_inserter(compressed));
+
+ fclose(f);
+
+ if (!PNGCodec::Decode(&compressed[0], compressed.size(),
+ PNGCodec::FORMAT_RGBA, &m_data, &m_width, &m_height)) {
+ clear();
+ return false;
+ }
+ return true;
+ }
+
+ void clear()
+ {
+ m_width = m_height = 0;
+ m_data.clear();
+ }
+
+ // Returns the RGBA value of the pixel at the given location
+ const uint32_t pixelAt(int x, int y) const
+ {
+ ASSERT(x >= 0 && x < m_width);
+ ASSERT(y >= 0 && y < m_height);
+ return *reinterpret_cast<const uint32_t*>(&(m_data[(y * m_width + x) * 4]));
+ }
+
+ void setPixelAt(int x, int y, uint32_t color) const
+ {
+ ASSERT(x >= 0 && x < m_width);
+ ASSERT(y >= 0 && y < m_height);
+ void* addr = &const_cast<unsigned char*>(&m_data.front())[(y * m_width + x) * 4];
+ *reinterpret_cast<uint32_t*>(addr) = color;
+ }
+
+private:
+ // pixel dimensions of the image
+ int m_width, m_height;
+
+ vector<unsigned char> m_data;
+};
+
+float percentageDifferent(const Image& baseline, const Image& actual)
+{
+ int w = min(baseline.width(), actual.width());
+ int h = min(baseline.height(), actual.height());
+
+ // Compute pixels different in the overlap
+ int pixelsDifferent = 0;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ if (baseline.pixelAt(x, y) != actual.pixelAt(x, y))
+ pixelsDifferent++;
+ }
+ }
+
+ // Count pixels that are a difference in size as also being different
+ int maxWidth = max(baseline.width(), actual.width());
+ int maxHeight = max(baseline.height(), actual.height());
+
+ // ...pixels off the right side, but not including the lower right corner
+ pixelsDifferent += (maxWidth - w) * h;
+
+ // ...pixels along the bottom, including the lower right corner
+ pixelsDifferent += (maxHeight - h) * maxWidth;
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ float totalPixels = static_cast<float>(actual.width()) * static_cast<float>(actual.height());
+ if (!totalPixels)
+ return 100.0f; // When the bitmap is empty, they are 100% different.
+ return static_cast<float>(pixelsDifferent) / totalPixels * 100;
+}
+
+void printHelp()
+{
+ fprintf(stderr,
+ "Usage:\n"
+ " ImageDiff <compare file> <reference file>\n"
+ " Compares two files on disk, returning 0 when they are the same\n"
+ " ImageDiff --use-stdin\n"
+ " Stays open reading pairs of filenames from stdin, comparing them,\n"
+ " and sending 0 to stdout when they are the same\n"
+ " ImageDiff --diff <compare file> <reference file> <output file>\n"
+ " Compares two files on disk, outputs an image that visualizes the"
+ " difference to <output file>\n");
+ /* For unfinished webkit-like-mode (see below)
+ "\n"
+ " ImageDiff -s\n"
+ " Reads stream input from stdin, should be EXACTLY of the format\n"
+ " \"Content-length: <byte length> <data>Content-length: ...\n"
+ " it will take as many file pairs as given, and will compare them as\n"
+ " (cmp_file, reference_file) pairs\n");
+ */
+}
+
+int compareImages(const char* file1, const char* file2)
+{
+ Image actualImage;
+ Image baselineImage;
+
+ if (!actualImage.createFromFilename(file1)) {
+ fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1);
+ return statusError;
+ }
+ if (!baselineImage.createFromFilename(file2)) {
+ fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2);
+ return statusError;
+ }
+
+ float percent = percentageDifferent(actualImage, baselineImage);
+ if (percent > 0.0) {
+ // failure: The WebKit version also writes the difference image to
+ // stdout, which seems excessive for our needs.
+ printf("diff: %01.2f%% failed\n", percent);
+ return statusDifferent;
+ }
+
+ // success
+ printf("diff: %01.2f%% passed\n", percent);
+ return statusSame;
+
+}
+
+// Untested mode that acts like WebKit's image comparator. I wrote this but
+// decided it's too complicated. We may use it in the future if it looks useful.
+int untestedCompareImages()
+{
+ Image actualImage;
+ Image baselineImage;
+ char buffer[2048];
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+ if (!strncmp("Content-length: ", buffer, 16)) {
+ char* context;
+#if OS(WINDOWS)
+ strtok_s(buffer, " ", &context);
+ int imageSize = strtol(strtok_s(0, " ", &context), 0, 10);
+#else
+ strtok_r(buffer, " ", &context);
+ int imageSize = strtol(strtok_r(0, " ", &context), 0, 10);
+#endif
+
+ bool success = false;
+ if (imageSize > 0 && !actualImage.hasImage()) {
+ if (!actualImage.craeteFromStdin(imageSize)) {
+ fputs("Error, input image can't be decoded.\n", stderr);
+ return 1;
+ }
+ } else if (imageSize > 0 && !baselineImage.hasImage()) {
+ if (!baselineImage.craeteFromStdin(imageSize)) {
+ fputs("Error, baseline image can't be decoded.\n", stderr);
+ return 1;
+ }
+ } else {
+ fputs("Error, image size must be specified.\n", stderr);
+ return 1;
+ }
+ }
+
+ if (actualImage.hasImage() && baselineImage.hasImage()) {
+ float percent = percentageDifferent(actualImage, baselineImage);
+ if (percent > 0.0) {
+ // failure: The WebKit version also writes the difference image to
+ // stdout, which seems excessive for our needs.
+ printf("diff: %01.2f%% failed\n", percent);
+ } else {
+ // success
+ printf("diff: %01.2f%% passed\n", percent);
+ }
+ actualImage.clear();
+ baselineImage.clear();
+ }
+ fflush(stdout);
+ }
+ return 0;
+}
+
+bool createImageDiff(const Image& image1, const Image& image2, Image* out)
+{
+ int w = min(image1.width(), image2.width());
+ int h = min(image1.height(), image2.height());
+ *out = Image(image1);
+ bool same = (image1.width() == image2.width()) && (image1.height() == image2.height());
+
+ // FIXME: do something with the extra pixels if the image sizes are different.
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ uint32_t basePixel = image1.pixelAt(x, y);
+ if (basePixel != image2.pixelAt(x, y)) {
+ // Set differing pixels red.
+ out->setPixelAt(x, y, rgbaRed | rgbaAlpha);
+ same = false;
+ } else {
+ // Set same pixels as faded.
+ uint32_t alpha = basePixel & rgbaAlpha;
+ uint32_t newPixel = basePixel - ((alpha / 2) & rgbaAlpha);
+ out->setPixelAt(x, y, newPixel);
+ }
+ }
+ }
+
+ return same;
+}
+
+static bool writeFile(const char* outFile, const unsigned char* data, size_t dataSize)
+{
+ FILE* file = fopen(outFile, "wb");
+ if (!file) {
+ fprintf(stderr, "ImageDiff: Unable to create file \"%s\"\n", file);
+ return false;
+ }
+ if (dataSize != fwrite(data, 1, dataSize, file)) {
+ fclose(file);
+ fprintf(stderr, "ImageDiff: Unable to write data to file \"%s\"\n", file);
+ return false;
+ }
+ fclose(file);
+ return true;
+}
+
+int diffImages(const char* file1, const char* file2, const char* outFile)
+{
+ Image actualImage;
+ Image baselineImage;
+
+ if (!actualImage.createFromFilename(file1)) {
+ fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1);
+ return statusError;
+ }
+ if (!baselineImage.createFromFilename(file2)) {
+ fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2);
+ return statusError;
+ }
+
+ Image diffImage;
+ bool same = createImageDiff(baselineImage, actualImage, &diffImage);
+ if (same)
+ return statusSame;
+
+ vector<unsigned char> pngData;
+ PNGCodec::Encode(diffImage.data(), PNGCodec::FORMAT_RGBA, diffImage.width(),
+ diffImage.height(), diffImage.width() * 4, false, &pngData);
+ if (!writeFile(outFile, &pngData.front(), pngData.size()))
+ return statusError;
+ return statusDifferent;
+}
+
+int main(int argc, const char* argv[])
+{
+ Vector<const char*> values;
+ bool pollStdin = false;
+ bool generateDiff = false;
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], optionPollStdin))
+ pollStdin = true;
+ else if (!strcmp(argv[i], optionGenerateDiff))
+ generateDiff = true;
+ else
+ values.append(argv[i]);
+ }
+
+ if (pollStdin) {
+ // Watch stdin for filenames.
+ const size_t bufferSize = PATH_MAX;
+ char stdinBuffer[bufferSize];
+ char firstName[bufferSize];
+ bool haveFirstName = false;
+ while (fgets(stdinBuffer, bufferSize, stdin)) {
+ if (!stdinBuffer[0])
+ continue;
+
+ if (haveFirstName) {
+ // compareImages writes results to stdout unless an error occurred.
+ if (compareImages(firstName, stdinBuffer) == statusError)
+ printf("error\n");
+ fflush(stdout);
+ haveFirstName = false;
+ } else {
+ // Save the first filename in another buffer and wait for the second
+ // filename to arrive via stdin.
+ strcpy(firstName, stdinBuffer);
+ haveFirstName = true;
+ }
+ }
+ return 0;
+ }
+
+ if (generateDiff) {
+ if (values.size() == 3)
+ return diffImages(values[0], values[1], values[2]);
+ } else if (values.size() == 2)
+ return compareImages(argv[1], argv[2]);
+
+ printHelp();
+ return statusError;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp
new file mode 100644
index 0000000..d0dca69
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LayoutTestController.h"
+
+#include "TestShell.h"
+#include "WebViewHost.h"
+#include "base/string_util.h"
+#include "public/WebAnimationController.h"
+#include "public/WebConsoleMessage.h"
+#include "public/WebDocument.h"
+#include "public/WebFrame.h"
+#include "public/WebInputElement.h"
+#include "public/WebKit.h"
+#include "public/WebScriptSource.h"
+#include "public/WebSecurityPolicy.h"
+#include "public/WebSettings.h"
+#include "public/WebSize.h"
+#include "public/WebURL.h"
+#include "public/WebView.h"
+#include "webkit/support/webkit_support.h"
+
+#if OS(WINDOWS)
+#include <wtf/OwnArrayPtr.h>
+#endif
+
+using namespace WebKit;
+using namespace std;
+
+LayoutTestController::LayoutTestController(TestShell* shell)
+ : m_timeoutFactory(this)
+ , m_shell(shell)
+ , m_workQueue(this)
+{
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling bindToJavaScript() (defined
+ // by CppBoundClass, the parent to LayoutTestController).
+ bindMethod("dumpAsText", &LayoutTestController::dumpAsText);
+ bindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions);
+ bindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText);
+ bindMethod("dumpDatabaseCallbacks", &LayoutTestController::dumpDatabaseCallbacks);
+ bindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks);
+ bindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList);
+ bindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks);
+ bindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks);
+ bindMethod("dumpStatusCallbacks", &LayoutTestController::dumpWindowStatusChanges);
+ bindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges);
+ bindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing);
+ bindMethod("waitUntilDone", &LayoutTestController::waitUntilDone);
+ bindMethod("notifyDone", &LayoutTestController::notifyDone);
+ bindMethod("queueReload", &LayoutTestController::queueReload);
+ bindMethod("queueLoadingScript", &LayoutTestController::queueLoadingScript);
+ bindMethod("queueNonLoadingScript", &LayoutTestController::queueNonLoadingScript);
+ bindMethod("queueLoad", &LayoutTestController::queueLoad);
+ bindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation);
+ bindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation);
+ bindMethod("windowCount", &LayoutTestController::windowCount);
+ bindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows);
+ bindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete);
+ bindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual);
+ bindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies);
+ bindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey);
+ bindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements);
+ bindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation);
+ bindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled);
+ bindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource);
+ bindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag);
+ bindMethod("execCommand", &LayoutTestController::execCommand);
+ bindMethod("isCommandEnabled", &LayoutTestController::isCommandEnabled);
+ bindMethod("setPopupBlockingEnabled", &LayoutTestController::setPopupBlockingEnabled);
+ bindMethod("setStopProvisionalFrameLoads", &LayoutTestController::setStopProvisionalFrameLoads);
+ bindMethod("setSmartInsertDeleteEnabled", &LayoutTestController::setSmartInsertDeleteEnabled);
+ bindMethod("setSelectTrailingWhitespaceEnabled", &LayoutTestController::setSelectTrailingWhitespaceEnabled);
+ bindMethod("pauseAnimationAtTimeOnElementWithId", &LayoutTestController::pauseAnimationAtTimeOnElementWithId);
+ bindMethod("pauseTransitionAtTimeOnElementWithId", &LayoutTestController::pauseTransitionAtTimeOnElementWithId);
+ bindMethod("elementDoesAutoCompleteForElementWithId", &LayoutTestController::elementDoesAutoCompleteForElementWithId);
+ bindMethod("numberOfActiveAnimations", &LayoutTestController::numberOfActiveAnimations);
+ bindMethod("disableImageLoading", &LayoutTestController::disableImageLoading);
+ bindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled);
+ bindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate);
+ bindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate);
+ bindMethod("setWillSendRequestReturnsNullOnRedirect", &LayoutTestController::setWillSendRequestReturnsNullOnRedirect);
+ bindMethod("setWillSendRequestReturnsNull", &LayoutTestController::setWillSendRequestReturnsNull);
+ bindMethod("addOriginAccessWhitelistEntry", &LayoutTestController::addOriginAccessWhitelistEntry);
+ bindMethod("clearAllDatabases", &LayoutTestController::clearAllDatabases);
+ bindMethod("setDatabaseQuota", &LayoutTestController::setDatabaseQuota);
+ bindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale);
+ bindMethod("counterValueForElementById", &LayoutTestController::counterValueForElementById);
+ bindMethod("addUserScript", &LayoutTestController::addUserScript);
+ bindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById);
+ bindMethod("numberOfPages", &LayoutTestController::numberOfPages);
+ bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect);
+
+ // The following are stubs.
+ bindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive);
+ bindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder);
+ bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect);
+ bindMethod("display", &LayoutTestController::display);
+ bindMethod("testRepaint", &LayoutTestController::testRepaint);
+ bindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally);
+ bindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList);
+ bindMethod("keepWebHistory", &LayoutTestController::keepWebHistory);
+ bindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject);
+ bindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject);
+ bindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf);
+ bindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL);
+ bindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews);
+ bindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled);
+ bindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode);
+
+ bindMethod("setXSSAuditorEnabled", &LayoutTestController::setXSSAuditorEnabled);
+ bindMethod("evaluateScriptInIsolatedWorld", &LayoutTestController::evaluateScriptInIsolatedWorld);
+ bindMethod("overridePreference", &LayoutTestController::overridePreference);
+ bindMethod("setAllowUniversalAccessFromFileURLs", &LayoutTestController::setAllowUniversalAccessFromFileURLs);
+ bindMethod("setAllowFileAccessFromFileURLs", &LayoutTestController::setAllowFileAccessFromFileURLs);
+ bindMethod("setTimelineProfilingEnabled", &LayoutTestController::setTimelineProfilingEnabled);
+ bindMethod("evaluateInWebInspector", &LayoutTestController::evaluateInWebInspector);
+ bindMethod("forceRedSelectionColors", &LayoutTestController::forceRedSelectionColors);
+
+ // The fallback method is called when an unknown method is invoked.
+ bindFallbackMethod(&LayoutTestController::fallbackMethod);
+
+ // Shared properties.
+ // globalFlag is used by a number of layout tests in
+ // LayoutTests\http\tests\security\dataURL.
+ bindProperty("globalFlag", &m_globalFlag);
+ // webHistoryItemCount is used by tests in LayoutTests\http\tests\history
+ bindProperty("webHistoryItemCount", &m_webHistoryItemCount);
+}
+
+LayoutTestController::WorkQueue::~WorkQueue()
+{
+ reset();
+}
+
+void LayoutTestController::WorkQueue::processWorkSoon()
+{
+ if (m_controller->m_shell->webViewHost()->topLoadingFrame())
+ return;
+
+ if (!m_queue.isEmpty()) {
+ // We delay processing queued work to avoid recursion problems.
+ m_timer.Start(base::TimeDelta(), this, &WorkQueue::processWork);
+ } else if (!m_controller->m_waitUntilDone) {
+ m_controller->m_shell->testFinished();
+ }
+}
+
+void LayoutTestController::WorkQueue::processWork()
+{
+ TestShell* shell = m_controller->m_shell;
+ // Quit doing work once a load is in progress.
+ while (!m_queue.isEmpty()) {
+ bool startedLoad = m_queue.first()->run(shell);
+ delete m_queue.first();
+ m_queue.removeFirst();
+ if (startedLoad)
+ return;
+ }
+
+ if (!m_controller->m_waitUntilDone && !shell->webViewHost()->topLoadingFrame())
+ shell->testFinished();
+}
+
+void LayoutTestController::WorkQueue::reset()
+{
+ m_frozen = false;
+ while (!m_queue.isEmpty()) {
+ delete m_queue.first();
+ m_queue.removeFirst();
+ }
+}
+
+void LayoutTestController::WorkQueue::addWork(WorkItem* work)
+{
+ if (m_frozen) {
+ delete work;
+ return;
+ }
+ m_queue.append(work);
+}
+
+void LayoutTestController::dumpAsText(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpAsText = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpDatabaseCallbacks(const CppArgumentList&, CppVariant* result)
+{
+ // Do nothing; we don't use this flag anywhere for now
+ result->setNull();
+}
+
+void LayoutTestController::dumpEditingCallbacks(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpEditingCallbacks = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpBackForwardList(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpBackForwardList = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpFrameLoadCallbacks = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpResourceLoadCallbacks = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpChildFrameScrollPositions = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpChildFramesAsText(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpChildFramesAsText = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpWindowStatusChanges(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpWindowStatusChanges = true;
+ result->setNull();
+}
+
+void LayoutTestController::dumpTitleChanges(const CppArgumentList&, CppVariant* result)
+{
+ m_dumpTitleChanges = true;
+ result->setNull();
+}
+
+void LayoutTestController::setAcceptsEditing(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_acceptsEditing = arguments[0].value.boolValue;
+ result->setNull();
+}
+
+void LayoutTestController::waitUntilDone(const CppArgumentList&, CppVariant* result)
+{
+ if (!webkit_support::BeingDebugged()) {
+ webkit_support::PostDelayedTaskFromHere(
+ m_timeoutFactory.NewRunnableMethod(&LayoutTestController::notifyDoneTimedOut),
+ m_shell->layoutTestTimeout());
+ }
+ m_waitUntilDone = true;
+ result->setNull();
+}
+
+void LayoutTestController::notifyDone(const CppArgumentList&, CppVariant* result)
+{
+ // Test didn't timeout. Kill the timeout timer.
+ m_timeoutFactory.RevokeAll();
+
+ completeNotifyDone(false);
+ result->setNull();
+}
+
+void LayoutTestController::notifyDoneTimedOut()
+{
+ completeNotifyDone(true);
+}
+
+void LayoutTestController::completeNotifyDone(bool isTimeout)
+{
+ if (m_waitUntilDone && !m_shell->webViewHost()->topLoadingFrame() && m_workQueue.isEmpty()) {
+ if (isTimeout)
+ m_shell->testTimedOut();
+ else
+ m_shell->testFinished();
+ }
+ m_waitUntilDone = false;
+}
+
+class WorkItemBackForward : public LayoutTestController::WorkItem {
+public:
+ WorkItemBackForward(int distance) : m_distance(distance) {}
+ bool run(TestShell* shell)
+ {
+ shell->goToOffset(m_distance);
+ return true; // FIXME: Did it really start a navigation?
+ }
+private:
+ int m_distance;
+};
+
+void LayoutTestController::queueBackNavigation(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isNumber())
+ m_workQueue.addWork(new WorkItemBackForward(-arguments[0].toInt32()));
+ result->setNull();
+}
+
+void LayoutTestController::queueForwardNavigation(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isNumber())
+ m_workQueue.addWork(new WorkItemBackForward(arguments[0].toInt32()));
+ result->setNull();
+}
+
+class WorkItemReload : public LayoutTestController::WorkItem {
+public:
+ bool run(TestShell* shell)
+ {
+ shell->reload();
+ return true;
+ }
+};
+
+void LayoutTestController::queueReload(const CppArgumentList&, CppVariant* result)
+{
+ m_workQueue.addWork(new WorkItemReload);
+ result->setNull();
+}
+
+class WorkItemLoadingScript : public LayoutTestController::WorkItem {
+public:
+ WorkItemLoadingScript(const string& script) : m_script(script) {}
+ bool run(TestShell* shell)
+ {
+ shell->webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script)));
+ return true; // FIXME: Did it really start a navigation?
+ }
+private:
+ string m_script;
+};
+
+class WorkItemNonLoadingScript : public LayoutTestController::WorkItem {
+public:
+ WorkItemNonLoadingScript(const string& script) : m_script(script) {}
+ bool run(TestShell* shell)
+ {
+ shell->webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script)));
+ return false;
+ }
+private:
+ string m_script;
+};
+
+void LayoutTestController::queueLoadingScript(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isString())
+ m_workQueue.addWork(new WorkItemLoadingScript(arguments[0].toString()));
+ result->setNull();
+}
+
+void LayoutTestController::queueNonLoadingScript(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isString())
+ m_workQueue.addWork(new WorkItemNonLoadingScript(arguments[0].toString()));
+ result->setNull();
+}
+
+class WorkItemLoad : public LayoutTestController::WorkItem {
+public:
+ WorkItemLoad(const WebURL& url, const WebString& target)
+ : m_url(url)
+ , m_target(target) {}
+ bool run(TestShell* shell)
+ {
+ shell->webViewHost()->loadURLForFrame(m_url, m_target);
+ return true; // FIXME: Did it really start a navigation?
+ }
+private:
+ WebURL m_url;
+ WebString m_target;
+};
+
+void LayoutTestController::queueLoad(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isString()) {
+ // FIXME: Implement WebURL::resolve() and avoid GURL.
+ GURL currentURL = m_shell->webView()->mainFrame()->url();
+ GURL fullURL = currentURL.Resolve(arguments[0].toString());
+
+ string target = "";
+ if (arguments.size() > 1 && arguments[1].isString())
+ target = arguments[1].toString();
+
+ m_workQueue.addWork(new WorkItemLoad(fullURL, WebString::fromUTF8(target)));
+ }
+ result->setNull();
+}
+
+void LayoutTestController::objCIdentityIsEqual(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() < 2) {
+ // This is the best we can do to return an error.
+ result->setNull();
+ return;
+ }
+ result->set(arguments[0].isEqual(arguments[1]));
+}
+
+void LayoutTestController::reset()
+{
+ if (m_shell) {
+ m_shell->webView()->setZoomLevel(false, 0);
+ m_shell->webView()->setTabKeyCyclesThroughElements(true);
+#if defined(OS_LINUX)
+ // (Constants copied because we can't depend on the header that defined
+ // them from this file.)
+ m_shell->webView()->setSelectionColors(0xff1e90ff, 0xff000000, 0xffc8c8c8, 0xff323232);
+#endif // defined(OS_LINUX)
+ m_shell->webView()->removeAllUserContent();
+ }
+ m_dumpAsText = false;
+ m_dumpEditingCallbacks = false;
+ m_dumpFrameLoadCallbacks = false;
+ m_dumpResourceLoadCallbacks = false;
+ m_dumpBackForwardList = false;
+ m_dumpChildFrameScrollPositions = false;
+ m_dumpChildFramesAsText = false;
+ m_dumpWindowStatusChanges = false;
+ m_dumpSelectionRect = false;
+ m_dumpTitleChanges = false;
+ m_acceptsEditing = true;
+ m_waitUntilDone = false;
+ m_canOpenWindows = false;
+ m_testRepaint = false;
+ m_sweepHorizontally = false;
+ m_shouldAddFileToPasteboard = false;
+ m_stopProvisionalFrameLoads = false;
+ m_globalFlag.set(false);
+ m_webHistoryItemCount.set(0);
+
+ webkit_support::SetAcceptAllCookies(false);
+ WebSecurityPolicy::resetOriginAccessWhiteLists();
+
+ // Reset the default quota for each origin to 5MB
+ webkit_support::SetDatabaseQuota(5 * 1024 * 1024);
+
+ setlocale(LC_ALL, "");
+
+ if (m_closeRemainingWindows)
+ m_shell->closeRemainingWindows();
+ else
+ m_closeRemainingWindows = true;
+ m_workQueue.reset();
+}
+
+void LayoutTestController::locationChangeDone()
+{
+ m_webHistoryItemCount.set(m_shell->navigationEntryCount());
+
+ // No more new work after the first complete load.
+ m_workQueue.setFrozen(true);
+
+ if (!m_waitUntilDone)
+ m_workQueue.processWorkSoon();
+}
+
+void LayoutTestController::policyDelegateDone()
+{
+ ASSERT(m_waitUntilDone);
+ m_shell->testFinished();
+ m_waitUntilDone = false;
+}
+
+void LayoutTestController::setCanOpenWindows(const CppArgumentList&, CppVariant* result)
+{
+ m_canOpenWindows = true;
+ result->setNull();
+}
+
+void LayoutTestController::setTabKeyCyclesThroughElements(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webView()->setTabKeyCyclesThroughElements(arguments[0].toBoolean());
+ result->setNull();
+}
+
+void LayoutTestController::windowCount(const CppArgumentList&, CppVariant* result)
+{
+ result->set(static_cast<int>(m_shell->windowCount()));
+}
+
+void LayoutTestController::setCloseRemainingWindowsWhenComplete(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_closeRemainingWindows = arguments[0].value.boolValue;
+ result->setNull();
+}
+
+void LayoutTestController::setAlwaysAcceptCookies(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0)
+ webkit_support::SetAcceptAllCookies(cppVariantToBool(arguments[0]));
+ result->setNull();
+}
+
+void LayoutTestController::setWindowIsKey(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->setFocus(m_shell->webView(), arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::setUserStyleSheetEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webView()->settings()->setUserStyleSheetLocation(arguments[0].value.boolValue ? m_userStyleSheetLocation : WebURL());
+ result->setNull();
+}
+
+void LayoutTestController::setUserStyleSheetLocation(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isString()) {
+ m_userStyleSheetLocation = webkit_support::RewriteLayoutTestsURL(arguments[0].toString());
+ m_shell->webView()->settings()->setUserStyleSheetLocation(m_userStyleSheetLocation);
+ }
+ result->setNull();
+}
+
+void LayoutTestController::execCommand(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() <= 0 || !arguments[0].isString())
+ return;
+
+ std::string command = arguments[0].toString();
+ std::string value("");
+ // Ignore the second parameter (which is userInterface)
+ // since this command emulates a manual action.
+ if (arguments.size() >= 3 && arguments[2].isString())
+ value = arguments[2].toString();
+
+ // Note: webkit's version does not return the boolean, so neither do we.
+ m_shell->webView()->focusedFrame()->executeCommand(WebString::fromUTF8(command), WebString::fromUTF8(value));
+}
+
+void LayoutTestController::isCommandEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() <= 0 || !arguments[0].isString()) {
+ result->setNull();
+ return;
+ }
+
+ std::string command = arguments[0].toString();
+ bool rv = m_shell->webView()->focusedFrame()->isCommandEnabled(WebString::fromUTF8(command));
+ result->set(rv);
+}
+
+void LayoutTestController::setPopupBlockingEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool()) {
+ bool blockPopups = arguments[0].toBoolean();
+ m_shell->webView()->settings()->setJavaScriptCanOpenWindowsAutomatically(!blockPopups);
+ }
+ result->setNull();
+}
+
+void LayoutTestController::setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant* result)
+{
+ // We have no need to support Dashboard Compatibility Mode (mac-only)
+ result->setNull();
+}
+
+void LayoutTestController::setCustomPolicyDelegate(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool()) {
+ bool enable = arguments[0].value.boolValue;
+ bool permissive = false;
+ if (arguments.size() > 1 && arguments[1].isBool())
+ permissive = arguments[1].value.boolValue;
+ m_shell->webViewHost()->setCustomPolicyDelegate(enable, permissive);
+ }
+ result->setNull();
+}
+
+void LayoutTestController::waitForPolicyDelegate(const CppArgumentList&, CppVariant* result)
+{
+ m_shell->webViewHost()->waitForPolicyDelegate();
+ m_waitUntilDone = true;
+ result->setNull();
+}
+
+void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webViewHost()->setBlockRedirects(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::setWillSendRequestReturnsNull(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webViewHost()->setRequestReturnNull(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::pathToLocalResource(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() <= 0 || !arguments[0].isString())
+ return;
+
+ string url = arguments[0].toString();
+#if OS(WINDOWS)
+ if (StartsWithASCII(url, "/tmp/", true)) {
+ // We want a temp file.
+ const unsigned tempPrefixLength = 5;
+ size_t bufferSize = MAX_PATH;
+ OwnArrayPtr<WCHAR> tempPath(new WCHAR[bufferSize]);
+ DWORD tempLength = ::GetTempPathW(bufferSize, tempPath.get());
+ if (tempLength + url.length() - tempPrefixLength + 1 > bufferSize) {
+ bufferSize = tempLength + url.length() - tempPrefixLength + 1;
+ tempPath.set(new WCHAR[bufferSize]);
+ tempLength = GetTempPathW(bufferSize, tempPath.get());
+ ASSERT(tempLength < bufferSize);
+ }
+ std::string resultPath(WebString(tempPath.get(), tempLength).utf8());
+ resultPath.append(url.substr(tempPrefixLength));
+ result->set(resultPath);
+ return;
+ }
+#endif
+
+ // Some layout tests use file://// which we resolve as a UNC path. Normalize
+ // them to just file:///.
+ while (StartsWithASCII(url, "file:////", false))
+ url = url.substr(0, 8) + url.substr(9);
+ result->set(webkit_support::RewriteLayoutTestsURL(url).spec());
+}
+
+void LayoutTestController::addFileToPasteboardOnDrag(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+ m_shouldAddFileToPasteboard = true;
+}
+
+void LayoutTestController::setStopProvisionalFrameLoads(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+ m_stopProvisionalFrameLoads = true;
+}
+
+void LayoutTestController::setSmartInsertDeleteEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webViewHost()->setSmartInsertDeleteEnabled(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::setSelectTrailingWhitespaceEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webViewHost()->setSelectTrailingWhitespaceEnabled(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const WebString& animationName, double time, const WebString& elementId)
+{
+ WebFrame* webFrame = m_shell->webView()->mainFrame();
+ if (!webFrame)
+ return false;
+
+ WebAnimationController* controller = webFrame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element = webFrame->document().getElementById(elementId);
+ if (element.isNull())
+ return false;
+ return controller->pauseAnimationAtTime(element, animationName, time);
+}
+
+bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const WebString& propertyName, double time, const WebString& elementId)
+{
+ WebFrame* webFrame = m_shell->webView()->mainFrame();
+ if (!webFrame)
+ return false;
+
+ WebAnimationController* controller = webFrame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element = webFrame->document().getElementById(elementId);
+ if (element.isNull())
+ return false;
+ return controller->pauseTransitionAtTime(element, propertyName, time);
+}
+
+bool LayoutTestController::elementDoesAutoCompleteForElementWithId(const WebString& elementId)
+{
+ WebFrame* webFrame = m_shell->webView()->mainFrame();
+ if (!webFrame)
+ return false;
+
+ WebElement element = webFrame->document().getElementById(elementId);
+ if (element.isNull() || !element.hasTagName("input"))
+ return false;
+
+ WebInputElement inputElement = element.toElement<WebInputElement>();
+ return inputElement.autoComplete();
+}
+
+int LayoutTestController::numberOfActiveAnimations()
+{
+ WebFrame* webFrame = m_shell->webView()->mainFrame();
+ if (!webFrame)
+ return -1;
+
+ WebAnimationController* controller = webFrame->animationController();
+ if (!controller)
+ return -1;
+
+ return controller->numberOfActiveAnimations();
+}
+
+void LayoutTestController::pauseAnimationAtTimeOnElementWithId(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->set(false);
+ if (arguments.size() > 2 && arguments[0].isString() && arguments[1].isNumber() && arguments[2].isString()) {
+ WebString animationName = cppVariantToWebString(arguments[0]);
+ double time = arguments[1].toDouble();
+ WebString elementId = cppVariantToWebString(arguments[2]);
+ result->set(pauseAnimationAtTimeOnElementWithId(animationName, time, elementId));
+ }
+}
+
+void LayoutTestController::pauseTransitionAtTimeOnElementWithId(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->set(false);
+ if (arguments.size() > 2 && arguments[0].isString() && arguments[1].isNumber() && arguments[2].isString()) {
+ WebString propertyName = cppVariantToWebString(arguments[0]);
+ double time = arguments[1].toDouble();
+ WebString elementId = cppVariantToWebString(arguments[2]);
+ result->set(pauseTransitionAtTimeOnElementWithId(propertyName, time, elementId));
+ }
+}
+
+void LayoutTestController::elementDoesAutoCompleteForElementWithId(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() != 1 || !arguments[0].isString()) {
+ result->set(false);
+ return;
+ }
+ WebString elementId = cppVariantToWebString(arguments[0]);
+ result->set(elementDoesAutoCompleteForElementWithId(elementId));
+}
+
+void LayoutTestController::numberOfActiveAnimations(const CppArgumentList&, CppVariant* result)
+{
+ result->set(numberOfActiveAnimations());
+}
+
+void LayoutTestController::disableImageLoading(const CppArgumentList&, CppVariant* result)
+{
+ m_shell->webView()->settings()->setLoadsImagesAutomatically(false);
+ result->setNull();
+}
+
+void LayoutTestController::setIconDatabaseEnabled(const CppArgumentList&, CppVariant* result)
+{
+ // We don't use the WebKit icon database.
+ result->setNull();
+}
+
+//
+// Unimplemented stubs
+//
+
+void LayoutTestController::dumpAsWebArchive(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::setMainFrameIsFirstResponder(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::dumpSelectionRect(const CppArgumentList& arguments, CppVariant* result)
+{
+ m_dumpSelectionRect = true;
+ result->setNull();
+}
+
+void LayoutTestController::display(const CppArgumentList& arguments, CppVariant* result)
+{
+ WebViewHost* host = m_shell->webViewHost();
+ const WebKit::WebSize& size = m_shell->webView()->size();
+ WebRect rect(0, 0, size.width, size.height);
+ host->updatePaintRect(rect);
+ host->paintInvalidatedRegion();
+ host->displayRepaintMask();
+ result->setNull();
+}
+
+void LayoutTestController::testRepaint(const CppArgumentList&, CppVariant* result)
+{
+ m_testRepaint = true;
+ result->setNull();
+}
+
+void LayoutTestController::repaintSweepHorizontally(const CppArgumentList&, CppVariant* result)
+{
+ m_sweepHorizontally = true;
+ result->setNull();
+}
+
+void LayoutTestController::clearBackForwardList(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::keepWebHistory(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::storeWebScriptObject(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::accessStoredWebScriptObject(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::objCClassNameOf(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::addDisallowedURL(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+void LayoutTestController::setCallCloseOnWebViews(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::setPrivateBrowsingEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+}
+
+void LayoutTestController::setXSSAuditorEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webView()->settings()->setXSSAuditorEnabled(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::evaluateScriptInIsolatedWorld(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() >= 2 && arguments[0].isNumber() && arguments[1].isString()) {
+ WebScriptSource source(WebString::fromUTF8(arguments[1].toString()));
+ // This relies on the iframe focusing itself when it loads. This is a bit
+ // sketchy, but it seems to be what other tests do.
+ m_shell->webView()->focusedFrame()->executeScriptInIsolatedWorld(arguments[0].toInt32(), &source, 1, 1);
+ }
+ result->setNull();
+}
+
+void LayoutTestController::setAllowUniversalAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webView()->settings()->setAllowUniversalAccessFromFileURLs(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+void LayoutTestController::setAllowFileAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result)
+{
+ if (arguments.size() > 0 && arguments[0].isBool())
+ m_shell->webView()->settings()->setAllowFileAccessFromFileURLs(arguments[0].value.boolValue);
+ result->setNull();
+}
+
+// Need these conversions because the format of the value for booleans
+// may vary - for example, on mac "1" and "0" are used for boolean.
+bool LayoutTestController::cppVariantToBool(const CppVariant& value)
+{
+ if (value.isBool())
+ return value.toBoolean();
+ if (value.isInt32())
+ return value.toInt32();
+ if (value.isString()) {
+ string valueString = value.toString();
+ if (valueString == "true" || valueString == "1")
+ return true;
+ if (valueString == "false" || valueString == "0")
+ return false;
+ }
+ logErrorToConsole("Invalid value. Expected boolean value.");
+ return false;
+}
+
+int32_t LayoutTestController::cppVariantToInt32(const CppVariant& value)
+{
+ if (value.isInt32())
+ return value.toInt32();
+ if (value.isString()) {
+ int number;
+ if (StringToInt(value.toString(), &number))
+ return number;
+ }
+ logErrorToConsole("Invalid value for preference. Expected integer value.");
+ return 0;
+}
+
+WebString LayoutTestController::cppVariantToWebString(const CppVariant& value)
+{
+ if (!value.isString()) {
+ logErrorToConsole("Invalid value for preference. Expected string value.");
+ return WebString();
+ }
+ return WebString::fromUTF8(value.toString());
+}
+
+void LayoutTestController::overridePreference(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() != 2 || !arguments[0].isString())
+ return;
+
+ string key = arguments[0].toString();
+ CppVariant value = arguments[1];
+ WebSettings* settings = m_shell->webView()->settings();
+ if (key == "WebKitStandardFont")
+ settings->setStandardFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitFixedFont")
+ settings->setFixedFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitSerifFont")
+ settings->setSerifFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitSansSerifFont")
+ settings->setSansSerifFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitCursiveFont")
+ settings->setCursiveFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitFantasyFont")
+ settings->setFantasyFontFamily(cppVariantToWebString(value));
+ else if (key == "WebKitDefaultFontSize")
+ settings->setDefaultFontSize(cppVariantToInt32(value));
+ else if (key == "WebKitDefaultFixedFontSize")
+ settings->setDefaultFixedFontSize(cppVariantToInt32(value));
+ else if (key == "WebKitMinimumFontSize")
+ settings->setMinimumFontSize(cppVariantToInt32(value));
+ else if (key == "WebKitMinimumLogicalFontSize")
+ settings->setMinimumLogicalFontSize(cppVariantToInt32(value));
+ else if (key == "WebKitDefaultTextEncodingName")
+ settings->setDefaultTextEncodingName(cppVariantToWebString(value));
+ else if (key == "WebKitJavaScriptEnabled")
+ settings->setJavaScriptEnabled(cppVariantToBool(value));
+ else if (key == "WebKitWebSecurityEnabled")
+ settings->setWebSecurityEnabled(cppVariantToBool(value));
+ else if (key == "WebKitJavaScriptCanOpenWindowsAutomatically")
+ settings->setJavaScriptCanOpenWindowsAutomatically(cppVariantToBool(value));
+ else if (key == "WebKitDisplayImagesKey")
+ settings->setLoadsImagesAutomatically(cppVariantToBool(value));
+ else if (key == "WebKitPluginsEnabled")
+ settings->setPluginsEnabled(cppVariantToBool(value));
+ else if (key == "WebKitDOMPasteAllowedPreferenceKey")
+ settings->setDOMPasteAllowed(cppVariantToBool(value));
+ else if (key == "WebKitDeveloperExtrasEnabledPreferenceKey")
+ settings->setDeveloperExtrasEnabled(cppVariantToBool(value));
+ else if (key == "WebKitShrinksStandaloneImagesToFit")
+ settings->setShrinksStandaloneImagesToFit(cppVariantToBool(value));
+ else if (key == "WebKitTextAreasAreResizable")
+ settings->setTextAreasAreResizable(cppVariantToBool(value));
+ else if (key == "WebKitJavaEnabled")
+ settings->setJavaEnabled(cppVariantToBool(value));
+ else if (key == "WebKitUsesPageCachePreferenceKey")
+ settings->setUsesPageCache(cppVariantToBool(value));
+ else if (key == "WebKitXSSAuditorEnabled")
+ settings->setXSSAuditorEnabled(cppVariantToBool(value));
+ else if (key == "WebKitLocalStorageEnabledPreferenceKey")
+ settings->setLocalStorageEnabled(cppVariantToBool(value));
+ else if (key == "WebKitOfflineWebApplicationCacheEnabled")
+ settings->setOfflineWebApplicationCacheEnabled(cppVariantToBool(value));
+ else if (key == "WebKitTabToLinksPreferenceKey")
+ m_shell->webView()->setTabsToLinks(cppVariantToBool(value));
+ else if (key == "WebKitWebGLEnabled")
+ settings->setExperimentalWebGLEnabled(cppVariantToBool(value));
+ else {
+ string message("Invalid name for preference: ");
+ message.append(key);
+ logErrorToConsole(message);
+ }
+}
+
+void LayoutTestController::fallbackMethod(const CppArgumentList&, CppVariant* result)
+{
+ printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on LayoutTestController\n");
+ result->setNull();
+}
+
+void LayoutTestController::addOriginAccessWhitelistEntry(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ if (arguments.size() != 4 || !arguments[0].isString() || !arguments[1].isString()
+ || !arguments[2].isString() || !arguments[3].isBool())
+ return;
+
+ WebKit::WebURL url(GURL(arguments[0].toString()));
+ if (!url.isValid())
+ return;
+
+ WebSecurityPolicy::whiteListAccessFromOrigin(url,
+ WebString::fromUTF8(arguments[1].toString()),
+ WebString::fromUTF8(arguments[2].toString()),
+ arguments[3].toBoolean());
+}
+
+void LayoutTestController::clearAllDatabases(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ webkit_support::ClearAllDatabases();
+}
+
+void LayoutTestController::setDatabaseQuota(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if ((arguments.size() >= 1) && arguments[0].isInt32())
+ webkit_support::SetDatabaseQuota(arguments[0].toInt32());
+}
+
+void LayoutTestController::setPOSIXLocale(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() == 1 && arguments[0].isString())
+ setlocale(LC_ALL, arguments[0].toString().c_str());
+}
+
+void LayoutTestController::counterValueForElementById(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() < 1 || !arguments[0].isString())
+ return;
+ WebFrame* frame = m_shell->webView()->mainFrame();
+ if (!frame)
+ return;
+ WebString counterValue = frame->counterValueForElementById(cppVariantToWebString(arguments[0]));
+ if (counterValue.isNull())
+ return;
+ result->set(counterValue.utf8());
+}
+
+static bool parsePageSizeParameters(const CppArgumentList& arguments,
+ int argOffset,
+ float* pageWidthInPixels,
+ float* pageHeightInPixels)
+{
+ // WebKit is using the window width/height of DumpRenderTree as the
+ // default value of the page size.
+ // FIXME: share these values with other ports.
+ *pageWidthInPixels = 800;
+ *pageHeightInPixels = 600;
+ switch (arguments.size() - argOffset) {
+ case 2:
+ if (!arguments[argOffset].isNumber() || !arguments[1 + argOffset].isNumber())
+ return false;
+ *pageWidthInPixels = static_cast<float>(arguments[argOffset].toInt32());
+ *pageHeightInPixels = static_cast<float>(arguments[1 + argOffset].toInt32());
+ // fall through.
+ case 0:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void LayoutTestController::pageNumberForElementById(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ float pageWidthInPixels = 0;
+ float pageHeightInPixels = 0;
+ if (!parsePageSizeParameters(arguments, 1,
+ &pageWidthInPixels, &pageHeightInPixels))
+ return;
+ if (!arguments[0].isString())
+ return;
+ WebFrame* frame = m_shell->webView()->mainFrame();
+ if (!frame)
+ return;
+ result->set(frame->pageNumberForElementById(cppVariantToWebString(arguments[0]),
+ pageWidthInPixels, pageHeightInPixels));
+}
+
+void LayoutTestController::numberOfPages(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ float pageWidthInPixels = 0;
+ float pageHeightInPixels = 0;
+ if (!parsePageSizeParameters(arguments, 0, &pageWidthInPixels, &pageHeightInPixels))
+ return;
+
+ WebFrame* frame = m_shell->webView()->mainFrame();
+ if (!frame)
+ return;
+ WebSize size(pageWidthInPixels, pageHeightInPixels);
+ int numberOfPages = frame->printBegin(size);
+ frame->printEnd();
+ result->set(numberOfPages);
+}
+
+void LayoutTestController::logErrorToConsole(const std::string& text)
+{
+ m_shell->webViewHost()->didAddMessageToConsole(
+ WebConsoleMessage(WebConsoleMessage::LevelError, WebString::fromUTF8(text)),
+ WebString(), 0);
+}
+
+void LayoutTestController::setTimelineProfilingEnabled(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() < 1 || !arguments[0].isBool())
+ return;
+ // FIXME: Should call TestShellDevToolsAgent::setTimelineProfilingEnabled().
+}
+
+void LayoutTestController::evaluateInWebInspector(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() < 2 || !arguments[0].isInt32() || !arguments[1].isString())
+ return;
+ // FIXME: Should call TestShellDevToolsAgent::evaluateInWebInspector().
+}
+
+void LayoutTestController::forceRedSelectionColors(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ m_shell->webView()->setSelectionColors(0xffee0000, 0xff00ee00, 0xff000000, 0xffc0c0c0);
+}
+
+void LayoutTestController::addUserScript(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+ if (arguments.size() < 1 || !arguments[0].isString() || !arguments[1].isBool())
+ return;
+ m_shell->webView()->addUserScript(WebString::fromUTF8(arguments[0].toString()), arguments[1].toBoolean());
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h
new file mode 100644
index 0000000..a8639da
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ LayoutTestController class:
+ Bound to a JavaScript window.layoutTestController object using the
+ CppBoundClass::bindToJavascript(), this allows layout tests that are run in
+ the test_shell (or, in principle, any web page loaded into a client app built
+ with this class) to control various aspects of how the tests are run and what
+ sort of output they produce.
+*/
+
+#ifndef LayoutTestController_h
+#define LayoutTestController_h
+
+#include "CppBoundClass.h"
+#include "base/timer.h" // FIXME: Remove this.
+#include "public/WebString.h"
+#include "public/WebURL.h"
+#include <wtf/Deque.h>
+
+class TestShell;
+
+class LayoutTestController : public CppBoundClass {
+public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ LayoutTestController(TestShell*);
+
+ // This function sets a flag that tells the test_shell to dump pages as
+ // plain text, rather than as a text representation of the renderer's state.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpAsText(const CppArgumentList&, CppVariant*);
+
+ // This function should set a flag that tells the test_shell to print a line
+ // of descriptive text for each database command. It should take no
+ // arguments, and ignore any that may be present. However, at the moment, we
+ // don't have any DB function that prints messages, so for now this function
+ // doesn't do anything.
+ void dumpDatabaseCallbacks(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive text for each editing command. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpEditingCallbacks(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive text for each frame load callback. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to print out a text
+ // representation of the back/forward list. It ignores all arguments.
+ void dumpBackForwardList(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to print out the
+ // scroll offsets of the child frames. It ignores all.
+ void dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to recursively
+ // dump all frames as plain text if the dumpAsText flag is set.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpChildFramesAsText(const CppArgumentList&, CppVariant*);
+
+ // This function sets a flag that tells the test_shell to dump all calls
+ // to window.status().
+ // It takes no arguments, and ignores any that may be present.
+ void dumpWindowStatusChanges(const CppArgumentList&, CppVariant*);
+
+ // When called with a boolean argument, this sets a flag that controls
+ // whether content-editable elements accept editing focus when an editing
+ // attempt is made. It ignores any additional arguments.
+ void setAcceptsEditing(const CppArgumentList&, CppVariant*);
+
+ // Functions for dealing with windows. By default we block all new windows.
+ void windowCount(const CppArgumentList&, CppVariant*);
+ void setCanOpenWindows(const CppArgumentList&, CppVariant*);
+ void setCloseRemainingWindowsWhenComplete(const CppArgumentList&, CppVariant*);
+
+ // By default, tests end when page load is complete. These methods are used
+ // to delay the completion of the test until notifyDone is called.
+ void waitUntilDone(const CppArgumentList&, CppVariant*);
+ void notifyDone(const CppArgumentList&, CppVariant*);
+ void notifyDoneTimedOut();
+
+ // Methods for adding actions to the work queue. Used in conjunction with
+ // waitUntilDone/notifyDone above.
+ void queueBackNavigation(const CppArgumentList&, CppVariant*);
+ void queueForwardNavigation(const CppArgumentList&, CppVariant*);
+ void queueReload(const CppArgumentList&, CppVariant*);
+ void queueLoadingScript(const CppArgumentList&, CppVariant*);
+ void queueNonLoadingScript(const CppArgumentList&, CppVariant*);
+ void queueLoad(const CppArgumentList&, CppVariant*);
+
+ // Although this is named "objC" to match the Mac version, it actually tests
+ // the identity of its two arguments in C++.
+ void objCIdentityIsEqual(const CppArgumentList&, CppVariant*);
+
+ // Changes the cookie policy from the default to allow all cookies.
+ void setAlwaysAcceptCookies(const CppArgumentList&, CppVariant*);
+
+ // Gives focus to the window.
+ void setWindowIsKey(const CppArgumentList&, CppVariant*);
+
+ // Method that controls whether pressing Tab key cycles through page elements
+ // or inserts a '\t' char in text area
+ void setTabKeyCyclesThroughElements(const CppArgumentList&, CppVariant*);
+
+ // Passes through to WebPreferences which allows the user to have a custom
+ // style sheet.
+ void setUserStyleSheetEnabled(const CppArgumentList&, CppVariant*);
+ void setUserStyleSheetLocation(const CppArgumentList&, CppVariant*);
+
+ // Puts Webkit in "dashboard compatibility mode", which is used in obscure
+ // Mac-only circumstances. It's not really necessary, and will most likely
+ // never be used by Chrome, but some layout tests depend on its presence.
+ void setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant*);
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ void setCustomPolicyDelegate(const CppArgumentList&, CppVariant*);
+
+ // Delays completion of the test until the policy delegate runs.
+ void waitForPolicyDelegate(const CppArgumentList&, CppVariant*);
+
+ // Causes WillSendRequest to block redirects.
+ void setWillSendRequestReturnsNullOnRedirect(const CppArgumentList&, CppVariant*);
+
+ // Causes WillSendRequest to return an empty request.
+ void setWillSendRequestReturnsNull(const CppArgumentList&, CppVariant*);
+
+ // Converts a URL starting with file:///tmp/ to the local mapping.
+ void pathToLocalResource(const CppArgumentList&, CppVariant*);
+
+ // Sets a bool such that when a drag is started, we fill the drag clipboard
+ // with a fake file object.
+ void addFileToPasteboardOnDrag(const CppArgumentList&, CppVariant*);
+
+ // Executes an internal command (superset of document.execCommand() commands).
+ void execCommand(const CppArgumentList&, CppVariant*);
+
+ // Checks if an internal command is currently available.
+ void isCommandEnabled(const CppArgumentList&, CppVariant*);
+
+ // Set the WebPreference that controls webkit's popup blocking.
+ void setPopupBlockingEnabled(const CppArgumentList&, CppVariant*);
+
+ // If true, causes provisional frame loads to be stopped for the remainder of
+ // the test.
+ void setStopProvisionalFrameLoads(const CppArgumentList&, CppVariant*);
+
+ // Enable or disable smart insert/delete. This is enabled by default.
+ void setSmartInsertDeleteEnabled(const CppArgumentList&, CppVariant*);
+
+ // Enable or disable trailing whitespace selection on double click.
+ void setSelectTrailingWhitespaceEnabled(const CppArgumentList&, CppVariant*);
+
+ void pauseAnimationAtTimeOnElementWithId(const CppArgumentList&, CppVariant*);
+ void pauseTransitionAtTimeOnElementWithId(const CppArgumentList&, CppVariant*);
+ void elementDoesAutoCompleteForElementWithId(const CppArgumentList&, CppVariant*);
+ void numberOfActiveAnimations(const CppArgumentList&, CppVariant*);
+
+ void disableImageLoading(const CppArgumentList&, CppVariant*);
+
+ void setIconDatabaseEnabled(const CppArgumentList&, CppVariant*);
+
+ void dumpSelectionRect(const CppArgumentList&, CppVariant*);
+
+ // The following are only stubs. TODO(pamg): Implement any of these that
+ // are needed to pass the layout tests.
+ void dumpAsWebArchive(const CppArgumentList&, CppVariant*);
+ void dumpTitleChanges(const CppArgumentList&, CppVariant*);
+ void dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant*);
+ void setMainFrameIsFirstResponder(const CppArgumentList&, CppVariant*);
+ void display(const CppArgumentList&, CppVariant*);
+ void testRepaint(const CppArgumentList&, CppVariant*);
+ void repaintSweepHorizontally(const CppArgumentList&, CppVariant*);
+ void clearBackForwardList(const CppArgumentList&, CppVariant*);
+ void keepWebHistory(const CppArgumentList&, CppVariant*);
+ void storeWebScriptObject(const CppArgumentList&, CppVariant*);
+ void accessStoredWebScriptObject(const CppArgumentList&, CppVariant*);
+ void objCClassNameOf(const CppArgumentList&, CppVariant*);
+ void addDisallowedURL(const CppArgumentList&, CppVariant*);
+ void setCallCloseOnWebViews(const CppArgumentList&, CppVariant*);
+ void setPrivateBrowsingEnabled(const CppArgumentList&, CppVariant*);
+
+ void setXSSAuditorEnabled(const CppArgumentList&, CppVariant*);
+ void evaluateScriptInIsolatedWorld(const CppArgumentList&, CppVariant*);
+ void overridePreference(const CppArgumentList&, CppVariant*);
+ void setAllowUniversalAccessFromFileURLs(const CppArgumentList&, CppVariant*);
+ void setAllowFileAccessFromFileURLs(const CppArgumentList&, CppVariant*);
+
+
+ // The fallback method is called when a nonexistent method is called on
+ // the layout test controller object.
+ // It is usefull to catch typos in the JavaScript code (a few layout tests
+ // do have typos in them) and it allows the script to continue running in
+ // that case (as the Mac does).
+ void fallbackMethod(const CppArgumentList&, CppVariant*);
+
+ // Allows layout tests to call SecurityOrigin::addOriginAccessWhitelistEntry().
+ void addOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*);
+
+ // Clears all databases.
+ void clearAllDatabases(const CppArgumentList&, CppVariant*);
+ // Sets the default quota for all origins
+ void setDatabaseQuota(const CppArgumentList&, CppVariant*);
+
+ // Calls setlocale(LC_ALL, ...) for a specified locale.
+ // Resets between tests.
+ void setPOSIXLocale(const CppArgumentList&, CppVariant*);
+
+ // Gets the value of the counter in the element specified by its ID.
+ void counterValueForElementById(const CppArgumentList&, CppVariant*);
+
+ // Gets the number of page where the specified element will be put.
+ void pageNumberForElementById(const CppArgumentList&, CppVariant*);
+
+ // Gets the number of pages to be printed.
+ void numberOfPages(const CppArgumentList&, CppVariant*);
+
+
+ // Allows layout tests to start Timeline profiling.
+ void setTimelineProfilingEnabled(const CppArgumentList&, CppVariant*);
+
+ // Allows layout tests to exec scripts at WebInspector side.
+ void evaluateInWebInspector(const CppArgumentList&, CppVariant*);
+
+ // Forces the selection colors for testing under Linux.
+ void forceRedSelectionColors(const CppArgumentList&, CppVariant*);
+
+ // Adds a user script to be injected into new documents.
+ void addUserScript(const CppArgumentList&, CppVariant*);
+
+public:
+ // The following methods are not exposed to JavaScript.
+ void setWorkQueueFrozen(bool frozen) { m_workQueue.setFrozen(frozen); }
+
+ bool shouldDumpAsText() { return m_dumpAsText; }
+ bool shouldDumpEditingCallbacks() { return m_dumpEditingCallbacks; }
+ bool shouldDumpFrameLoadCallbacks() { return m_dumpFrameLoadCallbacks; }
+ void setShouldDumpFrameLoadCallbacks(bool value) { m_dumpFrameLoadCallbacks = value; }
+ bool shouldDumpResourceLoadCallbacks() {return m_dumpResourceLoadCallbacks; }
+ bool shouldDumpStatusCallbacks() { return m_dumpWindowStatusChanges; }
+ bool shouldDumpSelectionRect() { return m_dumpSelectionRect; }
+ bool shouldDumpBackForwardList() { return m_dumpBackForwardList; }
+ bool shouldDumpTitleChanges() { return m_dumpTitleChanges; }
+ bool shouldDumpChildFrameScrollPositions() { return m_dumpChildFrameScrollPositions; }
+ bool shouldDumpChildFramesAsText() { return m_dumpChildFramesAsText; }
+ bool acceptsEditing() { return m_acceptsEditing; }
+ bool canOpenWindows() { return m_canOpenWindows; }
+ bool shouldAddFileToPasteboard() { return m_shouldAddFileToPasteboard; }
+ bool stopProvisionalFrameLoads() { return m_stopProvisionalFrameLoads; }
+
+ bool testRepaint() const { return m_testRepaint; }
+ bool sweepHorizontally() const { return m_sweepHorizontally; }
+
+ // Called by the webview delegate when the toplevel frame load is done.
+ void locationChangeDone();
+
+ // Called by the webview delegate when the policy delegate runs if the
+ // waitForPolicyDelegate was called.
+ void policyDelegateDone();
+
+ // Reinitializes all static values. The reset() method should be called
+ // before the start of each test (currently from
+ // TestShell::runFileTest).
+ void reset();
+
+ // A single item in the work queue.
+ class WorkItem {
+ public:
+ virtual ~WorkItem() {}
+
+ // Returns true if this started a load.
+ virtual bool run(TestShell* shell) = 0;
+ };
+
+private:
+ friend class WorkItem;
+ friend class WorkQueue;
+
+ // Helper class for managing events queued by methods like queueLoad or
+ // queueScript.
+ class WorkQueue {
+ public:
+ WorkQueue(LayoutTestController* controller) : m_controller(controller) {}
+ virtual ~WorkQueue();
+ void processWorkSoon();
+
+ // Reset the state of the class between tests.
+ void reset();
+
+ void addWork(WorkItem* work);
+
+ void setFrozen(bool frozen) { m_frozen = frozen; }
+ bool isEmpty() { return m_queue.isEmpty(); }
+
+ private:
+ void processWork();
+
+ base::OneShotTimer<WorkQueue> m_timer;
+ Deque<WorkItem*> m_queue;
+ bool m_frozen;
+ LayoutTestController* m_controller;
+ };
+
+ // Support for overridePreference.
+ bool cppVariantToBool(const CppVariant&);
+ int32_t cppVariantToInt32(const CppVariant&);
+ WebKit::WebString cppVariantToWebString(const CppVariant&);
+
+ void logErrorToConsole(const std::string&);
+ void completeNotifyDone(bool isTimeout);
+
+ bool pauseAnimationAtTimeOnElementWithId(const WebKit::WebString& animationName, double time, const WebKit::WebString& elementId);
+ bool pauseTransitionAtTimeOnElementWithId(const WebKit::WebString& propertyName, double time, const WebKit::WebString& elementId);
+ bool elementDoesAutoCompleteForElementWithId(const WebKit::WebString&);
+ int numberOfActiveAnimations();
+
+ // Used for test timeouts.
+ ScopedRunnableMethodFactory<LayoutTestController> m_timeoutFactory;
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ TestShell* m_shell;
+
+ // If true, the test_shell will produce a plain text dump rather than a
+ // text representation of the renderer.
+ bool m_dumpAsText;
+
+ // If true, the test_shell will write a descriptive line for each editing
+ // command.
+ bool m_dumpEditingCallbacks;
+
+ // If true, the test_shell will draw the bounds of the current selection rect
+ // taking possible transforms of the selection rect into account.
+ bool m_dumpSelectionRect;
+
+ // If true, the test_shell will output a descriptive line for each frame
+ // load callback.
+ bool m_dumpFrameLoadCallbacks;
+
+ // If true, the test_shell will output a descriptive line for each resource
+ // load callback.
+ bool m_dumpResourceLoadCallbacks;
+
+ // If true, the test_shell will produce a dump of the back forward list as
+ // well.
+ bool m_dumpBackForwardList;
+
+ // If true, the test_shell will print out the child frame scroll offsets as
+ // well.
+ bool m_dumpChildFrameScrollPositions;
+
+ // If true and if dump_as_text_ is true, the test_shell will recursively
+ // dump all frames as plain text.
+ bool m_dumpChildFramesAsText;
+
+ // If true, the test_shell will dump all changes to window.status.
+ bool m_dumpWindowStatusChanges;
+
+ // If true, output a message when the page title is changed.
+ bool m_dumpTitleChanges;
+
+ // If true, the element will be treated as editable. This value is returned
+ // from various editing callbacks that are called just before edit operations
+ // are allowed.
+ bool m_acceptsEditing;
+
+ // If true, new windows can be opened via javascript or by plugins. By
+ // default, set to false and can be toggled to true using
+ // setCanOpenWindows().
+ bool m_canOpenWindows;
+
+ // When reset is called, go through and close all but the main test shell
+ // window. By default, set to true but toggled to false using
+ // setCloseRemainingWindowsWhenComplete().
+ bool m_closeRemainingWindows;
+
+ // If true, pixel dump will be produced as a series of 1px-tall, view-wide
+ // individual paints over the height of the view.
+ bool m_testRepaint;
+ // If true and test_repaint_ is true as well, pixel dump will be produced as
+ // a series of 1px-wide, view-tall paints across the width of the view.
+ bool m_sweepHorizontally;
+
+ // If true and a drag starts, adds a file to the drag&drop clipboard.
+ bool m_shouldAddFileToPasteboard;
+
+ // If true, stops provisional frame loads during the
+ // DidStartProvisionalLoadForFrame callback.
+ bool m_stopProvisionalFrameLoads;
+
+ // If true, don't dump output until notifyDone is called.
+ bool m_waitUntilDone;
+
+ // To prevent infinite loops, only the first page of a test can add to a
+ // work queue (since we may well come back to that same page).
+ bool m_workQueueFrozen;
+
+ WorkQueue m_workQueue;
+
+ CppVariant m_globalFlag;
+
+ // Bound variable counting the number of top URLs visited.
+ CppVariant m_webHistoryItemCount;
+
+ WebKit::WebURL m_userStyleSheetLocation;
+};
+
+#endif // LayoutTestController_h
diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm b/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm
new file mode 100644
index 0000000..e34cf5f
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <AppKit/AppKit.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// This is a simple helper app that changes the color sync profile to the
+// generic profile and back when done. This program is managed by the layout
+// test script, so it can do the job for multiple DumpRenderTree while they are
+// running layout tests.
+
+static CMProfileRef userColorProfile = 0;
+
+static void saveCurrentColorProfile()
+{
+ CGDirectDisplayID displayID = CGMainDisplayID();
+ CMProfileRef previousProfile;
+ CMError error = CMGetProfileByAVID((UInt32)displayID, &previousProfile);
+ if (error) {
+ NSLog(@"failed to get the current color profile, pixmaps won't match. "
+ @"Error: %d", (int)error);
+ } else {
+ userColorProfile = previousProfile;
+ }
+}
+
+static void installLayoutTestColorProfile()
+{
+ // To make sure we get consistent colors (not dependent on the Main display),
+ // we force the generic rgb color profile. This cases a change the user can
+ // see.
+
+ CGDirectDisplayID displayID = CGMainDisplayID();
+ NSColorSpace* genericSpace = [NSColorSpace genericRGBColorSpace];
+ CMProfileRef genericProfile = (CMProfileRef)[genericSpace colorSyncProfile];
+ CMError error = CMSetProfileByAVID((UInt32)displayID, genericProfile);
+ if (error) {
+ NSLog(@"failed install the generic color profile, pixmaps won't match. "
+ @"Error: %d", (int)error);
+ }
+}
+
+static void restoreUserColorProfile(void)
+{
+ if (!userColorProfile)
+ return;
+ CGDirectDisplayID displayID = CGMainDisplayID();
+ CMError error = CMSetProfileByAVID((UInt32)displayID, userColorProfile);
+ CMCloseProfile(userColorProfile);
+ if (error) {
+ NSLog(@"Failed to restore color profile, use System Preferences -> "
+ @"Displays -> Color to reset. Error: %d", (int)error);
+ }
+ userColorProfile = 0;
+}
+
+static void simpleSignalHandler(int sig)
+{
+ // Try to restore the color profile and try to go down cleanly
+ restoreUserColorProfile();
+ exit(128 + sig);
+}
+
+int main(int argc, char* argv[])
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ // Hooks the ways we might get told to clean up...
+ signal(SIGINT, simpleSignalHandler);
+ signal(SIGHUP, simpleSignalHandler);
+ signal(SIGTERM, simpleSignalHandler);
+
+ // Save off the current profile, and then install the layout test profile.
+ saveCurrentColorProfile();
+ installLayoutTestColorProfile();
+
+ // Let the script know we're ready
+ printf("ready\n");
+ fflush(stdout);
+
+ // Wait for any key (or signal)
+ getchar();
+
+ // Restore the profile
+ restoreUserColorProfile();
+
+ [pool release];
+ return 0;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp
new file mode 100644
index 0000000..fe70cab
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MockSpellCheck.h"
+
+#include "public/WebString.h"
+#include <wtf/ASCIICType.h>
+#include <wtf/Assertions.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+MockSpellCheck::MockSpellCheck()
+ : m_initialized(false) {}
+
+MockSpellCheck::~MockSpellCheck() {}
+
+static bool isNotASCIIAlpha(UChar ch) { return !isASCIIAlpha(ch); }
+
+bool MockSpellCheck::spellCheckWord(const WebString& text, int* misspelledOffset, int* misspelledLength)
+{
+ ASSERT(misspelledOffset);
+ ASSERT(misspelledLength);
+
+ // Initialize this spellchecker.
+ initializeIfNeeded();
+
+ // Reset the result values as our spellchecker does.
+ *misspelledOffset = 0;
+ *misspelledLength = 0;
+
+ // Convert to a String because we store String instances in
+ // m_misspelledWords and WebString has no find().
+ const String stringText(text.data(), text.length());
+
+ // Extract the first possible English word from the given string.
+ // The given string may include non-ASCII characters or numbers. So, we
+ // should filter out such characters before start looking up our
+ // misspelled-word table.
+ // (This is a simple version of our SpellCheckWordIterator class.)
+ // If the given string doesn't include any ASCII characters, we can treat the
+ // string as valid one.
+ // Unfortunately, This implementation splits a contraction, i.e. "isn't" is
+ // split into two pieces "isn" and "t". This is OK because webkit tests
+ // don't have misspelled contractions.
+ int wordOffset = stringText.find(isASCIIAlpha);
+ if (wordOffset == -1)
+ return true;
+ int wordEnd = stringText.find(isNotASCIIAlpha, wordOffset);
+ int wordLength = wordEnd == -1 ? stringText.length() - wordOffset : wordEnd - wordOffset;
+
+ // Look up our misspelled-word table to check if the extracted word is a
+ // known misspelled word, and return the offset and the length of the
+ // extracted word if this word is a known misspelled word.
+ // (See the comment in MockSpellCheck::initializeIfNeeded() why we use a
+ // misspelled-word table.)
+ String word = stringText.substring(wordOffset, wordLength);
+ if (!m_misspelledWords.contains(word))
+ return true;
+
+ *misspelledOffset = wordOffset;
+ *misspelledLength = wordLength;
+ return false;
+}
+
+bool MockSpellCheck::initializeIfNeeded()
+{
+ // Exit if we have already initialized this object.
+ if (m_initialized)
+ return false;
+
+ // Create a table that consists of misspelled words used in WebKit layout
+ // tests.
+ // Since WebKit layout tests don't have so many misspelled words as
+ // well-spelled words, it is easier to compare the given word with misspelled
+ // ones than to compare with well-spelled ones.
+ static const char* misspelledWords[] = {
+ // These words are known misspelled words in webkit tests.
+ // If there are other misspelled words in webkit tests, please add them in
+ // this array.
+ "foo",
+ "Foo",
+ "baz",
+ "fo",
+ "LibertyF",
+ "chello",
+ "xxxtestxxx",
+ "XXxxx",
+ "Textx",
+ "blockquoted",
+ "asd",
+ "Lorem",
+ "Nunc",
+ "Curabitur",
+ "eu",
+ "adlj",
+ "adaasj",
+ "sdklj",
+ "jlkds",
+ "jsaada",
+ "jlda",
+ "zz",
+ "contentEditable",
+ // The following words are used by unit tests.
+ "ifmmp",
+ "qwertyuiopasd",
+ "qwertyuiopasdf",
+ };
+
+ m_misspelledWords.clear();
+ for (size_t i = 0; i < arraysize(misspelledWords); ++i)
+ m_misspelledWords.add(String::fromUTF8(misspelledWords[i]), false);
+
+ // Mark as initialized to prevent this object from being initialized twice
+ // or more.
+ m_initialized = true;
+
+ // Since this MockSpellCheck class doesn't download dictionaries, this
+ // function always returns false.
+ return false;
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h
new file mode 100644
index 0000000..8c2ba92
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MockSpellCheck_h
+#define MockSpellCheck_h
+
+#include <wtf/HashMap.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+class WebString;
+}
+
+// A mock implementation of a spell-checker used for WebKit tests.
+// This class only implements the minimal functionarities required by WebKit
+// tests, i.e. this class just compares the given string with known misspelled
+// words in webkit tests and mark them as missspelled.
+// Even though this is sufficent for webkit tests, this class is not suitable
+// for any other usages.
+class MockSpellCheck {
+public:
+ MockSpellCheck();
+ ~MockSpellCheck();
+
+ // Checks the spellings of the specified text.
+ // This function returns true if the text consists of valid words, and
+ // returns false if it includes invalid words.
+ // When the given text includes invalid words, this function sets the
+ // position of the first invalid word to misspelledOffset, and the length of
+ // the first invalid word to misspelledLength, respectively.
+ // For example, when the given text is " zz zz", this function sets 3 to
+ // misspelledOffset and 2 to misspelledLength, respectively.
+ bool spellCheckWord(const WebKit::WebString& text,
+ int* misspelledOffset,
+ int* misspelledLength);
+
+private:
+ // Initialize the internal resources if we need to initialize it.
+ // Initializing this object may take long time. To prevent from hurting
+ // the performance of test_shell, we initialize this object when
+ // SpellCheckWord() is called for the first time.
+ // To be compliant with SpellCheck:InitializeIfNeeded(), this function
+ // returns true if this object is downloading a dictionary, otherwise
+ // it returns false.
+ bool initializeIfNeeded();
+
+ // A table that consists of misspelled words.
+ HashMap<WebCore::String, bool> m_misspelledWords;
+
+ // A flag representing whether or not this object is initialized.
+ bool m_initialized;
+};
+
+#endif // MockSpellCheck_h
diff --git a/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp b/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp
new file mode 100644
index 0000000..6e6cf11
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PlainTextController.h"
+
+#include "TestShell.h"
+#include "public/WebBindings.h"
+#include "public/WebRange.h"
+#include "public/WebString.h"
+
+using namespace WebKit;
+
+PlainTextController::PlainTextController()
+{
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling bindToJavaScript() (defined
+ // by CppBoundClass, the parent to PlainTextController).
+ bindMethod("plainText", &PlainTextController::plainText);
+
+ // The fallback method is called when an unknown method is invoked.
+ bindFallbackMethod(&PlainTextController::fallbackMethod);
+}
+
+void PlainTextController::plainText(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ if (arguments.size() < 1 || !arguments[0].isObject())
+ return;
+
+ // Check that passed-in object is, in fact, a range.
+ NPObject* npobject = NPVARIANT_TO_OBJECT(arguments[0]);
+ if (!npobject)
+ return;
+ WebRange range;
+ if (!WebBindings::getRange(npobject, &range))
+ return;
+
+ // Extract the text using the Range's text() method
+ WebString text = range.toPlainText();
+ result->set(text.utf8());
+}
+
+void PlainTextController::fallbackMethod(const CppArgumentList&, CppVariant* result)
+{
+ printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on PlainTextController\n");
+ result->setNull();
+}
+
diff --git a/WebKitTools/DumpRenderTree/chromium/PlainTextController.h b/WebKitTools/DumpRenderTree/chromium/PlainTextController.h
new file mode 100644
index 0000000..3d3a04c
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/PlainTextController.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PlainTextController_h
+#define PlainTextController_h
+
+#include "CppBoundClass.h"
+
+class TestShell;
+
+class PlainTextController : public CppBoundClass {
+public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ explicit PlainTextController();
+
+ // JS callback methods.
+ void plainText(const CppArgumentList&, CppVariant*);
+
+ // Fall-back method: called if an unknown method is invoked.
+ void fallbackMethod(const CppArgumentList&, CppVariant*);
+};
+
+#endif // PlainTextController_h
+
diff --git a/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp
new file mode 100644
index 0000000..8b4f954
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestNavigationController.h"
+
+#include "TestShell.h"
+#include <wtf/Assertions.h>
+
+using namespace WebKit;
+using namespace std;
+
+// ----------------------------------------------------------------------------
+// TestNavigationEntry
+
+TestNavigationEntry::TestNavigationEntry()
+ : m_pageID(-1) {}
+
+TestNavigationEntry::TestNavigationEntry(
+ int pageID, const WebURL& url, const WebString& title, const WebString& targetFrame)
+ : m_pageID(pageID)
+ , m_url(url)
+ , m_title(title)
+ , m_targetFrame(targetFrame) {}
+
+TestNavigationEntry::~TestNavigationEntry() {}
+
+void TestNavigationEntry::setContentState(const WebHistoryItem& state)
+{
+ m_state = state;
+}
+
+// ----------------------------------------------------------------------------
+// TestNavigationController
+
+TestNavigationController::TestNavigationController(NavigationHost* host)
+ : m_pendingEntry(0)
+ , m_lastCommittedEntryIndex(-1)
+ , m_pendingEntryIndex(-1)
+ , m_host(host)
+ , m_maxPageID(-1) {}
+
+TestNavigationController::~TestNavigationController()
+{
+ discardPendingEntry();
+}
+
+void TestNavigationController::reset()
+{
+ m_entries.clear();
+ discardPendingEntry();
+
+ m_lastCommittedEntryIndex = -1;
+}
+
+void TestNavigationController::reload()
+{
+ // Base the navigation on where we are now...
+ int currentIndex = currentEntryIndex();
+
+ // If we are no where, then we can't reload. TODO(darin): We should add a
+ // CanReload method.
+ if (currentIndex == -1)
+ return;
+
+ discardPendingEntry();
+
+ m_pendingEntryIndex = currentIndex;
+ navigateToPendingEntry(true);
+}
+
+void TestNavigationController::goToOffset(int offset)
+{
+ int index = m_lastCommittedEntryIndex + offset;
+ if (index < 0 || index >= entryCount())
+ return;
+
+ goToIndex(index);
+}
+
+void TestNavigationController::goToIndex(int index)
+{
+ ASSERT(index >= 0);
+ ASSERT(index < static_cast<int>(m_entries.size()));
+
+ discardPendingEntry();
+
+ m_pendingEntryIndex = index;
+ navigateToPendingEntry(false);
+}
+
+void TestNavigationController::loadEntry(TestNavigationEntry* entry)
+{
+ // When navigating to a new page, we don't know for sure if we will actually
+ // end up leaving the current page. The new page load could for example
+ // result in a download or a 'no content' response (e.g., a mailto: URL).
+ discardPendingEntry();
+ m_pendingEntry = entry;
+ navigateToPendingEntry(false);
+}
+
+
+TestNavigationEntry* TestNavigationController::lastCommittedEntry() const
+{
+ if (m_lastCommittedEntryIndex == -1)
+ return 0;
+ return m_entries[m_lastCommittedEntryIndex].get();
+}
+
+TestNavigationEntry* TestNavigationController::activeEntry() const
+{
+ TestNavigationEntry* entry = m_pendingEntry;
+ if (!entry)
+ entry = lastCommittedEntry();
+ return entry;
+}
+
+int TestNavigationController::currentEntryIndex() const
+{
+ if (m_pendingEntryIndex != -1)
+ return m_pendingEntryIndex;
+ return m_lastCommittedEntryIndex;
+}
+
+
+TestNavigationEntry* TestNavigationController::entryAtIndex(int index) const
+{
+ if (index < 0 || index >= entryCount())
+ return 0;
+ return m_entries[index].get();
+}
+
+TestNavigationEntry* TestNavigationController::entryWithPageID(int32_t pageID) const
+{
+ int index = entryIndexWithPageID(pageID);
+ return (index != -1) ? m_entries[index].get() : 0;
+}
+
+void TestNavigationController::didNavigateToEntry(TestNavigationEntry* entry)
+{
+ // If the entry is that of a page with PageID larger than any this Tab has
+ // seen before, then consider it a new navigation.
+ if (entry->pageID() > maxPageID()) {
+ insertEntry(entry);
+ return;
+ }
+
+ // Otherwise, we just need to update an existing entry with matching PageID.
+ // If the existing entry corresponds to the entry which is pending, then we
+ // must update the current entry index accordingly. When navigating to the
+ // same URL, a new PageID is not created.
+
+ int existingEntryIndex = entryIndexWithPageID(entry->pageID());
+ TestNavigationEntry* existingEntry = (existingEntryIndex != -1) ?
+ m_entries[existingEntryIndex].get() : 0;
+ if (!existingEntry) {
+ // No existing entry, then simply ignore this navigation!
+ } else if (existingEntry == m_pendingEntry) {
+ // The given entry might provide a new URL... e.g., navigating back to a
+ // page in session history could have resulted in a new client redirect.
+ existingEntry->setURL(entry->URL());
+ existingEntry->setContentState(entry->contentState());
+ m_lastCommittedEntryIndex = m_pendingEntryIndex;
+ m_pendingEntryIndex = -1;
+ m_pendingEntry = 0;
+ } else if (m_pendingEntry && m_pendingEntry->pageID() == -1
+ && GURL(m_pendingEntry->URL()) == GURL(existingEntry->URL().spec())) {
+ // Not a new navigation
+ discardPendingEntry();
+ } else {
+ // The given entry might provide a new URL... e.g., navigating to a page
+ // might result in a client redirect, which should override the URL of the
+ // existing entry.
+ existingEntry->setURL(entry->URL());
+ existingEntry->setContentState(entry->contentState());
+
+ // The navigation could have been issued by the renderer, so be sure that
+ // we update our current index.
+ m_lastCommittedEntryIndex = existingEntryIndex;
+ }
+
+ delete entry;
+ updateMaxPageID();
+}
+
+void TestNavigationController::discardPendingEntry()
+{
+ if (m_pendingEntryIndex == -1)
+ delete m_pendingEntry;
+ m_pendingEntry = 0;
+ m_pendingEntryIndex = -1;
+}
+
+void TestNavigationController::insertEntry(TestNavigationEntry* entry)
+{
+ discardPendingEntry();
+
+ // Prune any entry which are in front of the current entry
+ int currentSize = static_cast<int>(m_entries.size());
+ if (currentSize > 0) {
+ while (m_lastCommittedEntryIndex < (currentSize - 1)) {
+ m_entries.removeLast();
+ currentSize--;
+ }
+ }
+
+ m_entries.append(linked_ptr<TestNavigationEntry>(entry));
+ m_lastCommittedEntryIndex = static_cast<int>(m_entries.size()) - 1;
+ updateMaxPageID();
+}
+
+int TestNavigationController::entryIndexWithPageID(int32 pageID) const
+{
+ for (int i = static_cast<int>(m_entries.size()) - 1; i >= 0; --i) {
+ if (m_entries[i]->pageID() == pageID)
+ return i;
+ }
+ return -1;
+}
+
+void TestNavigationController::navigateToPendingEntry(bool reload)
+{
+ // For session history navigations only the pending_entry_index_ is set.
+ if (!m_pendingEntry) {
+ ASSERT(m_pendingEntryIndex != -1);
+ m_pendingEntry = m_entries[m_pendingEntryIndex].get();
+ }
+
+ if (m_host->navigate(*m_pendingEntry, reload)) {
+ // Note: this is redundant if navigation completed synchronously because
+ // DidNavigateToEntry call this as well.
+ updateMaxPageID();
+ } else
+ discardPendingEntry();
+}
+
+void TestNavigationController::updateMaxPageID()
+{
+ TestNavigationEntry* entry = activeEntry();
+ if (entry)
+ m_maxPageID = max(m_maxPageID, entry->pageID());
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h
new file mode 100644
index 0000000..bd3c2f4
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestNavigationController_h
+#define TestNavigationController_h
+
+#include "base/basictypes.h"
+#include "base/linked_ptr.h"
+#include "public/WebDataSource.h"
+#include "public/WebHistoryItem.h"
+#include "public/WebString.h"
+#include "public/WebURL.h"
+#include <string>
+#include <wtf/Vector.h>
+
+// Associated with browser-initated navigations to hold tracking data.
+class TestShellExtraData : public WebKit::WebDataSource::ExtraData {
+public:
+ TestShellExtraData(int32_t pendingPageID)
+ : pendingPageID(pendingPageID)
+ , requestCommitted(false) {}
+
+ // Contains the page_id for this navigation or -1 if there is none yet.
+ int32_t pendingPageID;
+
+ // True if we have already processed the "DidCommitLoad" event for this
+ // request. Used by session history.
+ bool requestCommitted;
+};
+
+// Stores one back/forward navigation state for the test shell.
+class TestNavigationEntry: public Noncopyable {
+public:
+ TestNavigationEntry();
+ TestNavigationEntry(int pageID,
+ const WebKit::WebURL&,
+ const WebKit::WebString& title,
+ const WebKit::WebString& targetFrame);
+
+ // Virtual to allow test_shell to extend the class.
+ ~TestNavigationEntry();
+
+ // Set / Get the URI
+ void setURL(const WebKit::WebURL& url) { m_url = url; }
+ const WebKit::WebURL& URL() const { return m_url; }
+
+ // Set / Get the title
+ void setTitle(const WebKit::WebString& title) { m_title = title; }
+ const WebKit::WebString& title() const { return m_title; }
+
+ // Set / Get a state.
+ void setContentState(const WebKit::WebHistoryItem&);
+ const WebKit::WebHistoryItem& contentState() const { return m_state; }
+
+ // Get the page id corresponding to the tab's state.
+ void setPageID(int pageID) { m_pageID = pageID; }
+ int32_t pageID() const { return m_pageID; }
+
+ const WebKit::WebString& targetFrame() const { return m_targetFrame; }
+
+private:
+ // Describes the current page that the tab represents. This is not relevant
+ // for all tab contents types.
+ int32_t m_pageID;
+
+ WebKit::WebURL m_url;
+ WebKit::WebString m_title;
+ WebKit::WebHistoryItem m_state;
+ WebKit::WebString m_targetFrame;
+};
+
+class NavigationHost {
+public:
+ virtual bool navigate(const TestNavigationEntry&, bool reload) = 0;
+};
+
+// Test shell's NavigationController. The goal is to be as close to the Chrome
+// version as possible.
+class TestNavigationController: public Noncopyable {
+public:
+ TestNavigationController(NavigationHost*);
+ ~TestNavigationController();
+
+ void reset();
+
+ // Causes the controller to reload the current (or pending) entry.
+ void reload();
+
+ // Causes the controller to go to the specified offset from current. Does
+ // nothing if out of bounds.
+ void goToOffset(int);
+
+ // Causes the controller to go to the specified index.
+ void goToIndex(int);
+
+ // Causes the controller to load the specified entry. The controller
+ // assumes ownership of the entry.
+ // NOTE: Do not pass an entry that the controller already owns!
+ void loadEntry(TestNavigationEntry*);
+
+ // Returns the last committed entry, which may be null if there are no
+ // committed entries.
+ TestNavigationEntry* lastCommittedEntry() const;
+
+ // Returns the number of entries in the NavigationControllerBase, excluding
+ // the pending entry if there is one.
+ int entryCount() const { return static_cast<int>(m_entries.size()); }
+
+ // Returns the active entry, which is the pending entry if a navigation is in
+ // progress or the last committed entry otherwise. NOTE: This can be 0!!
+ //
+ // If you are trying to get the current state of the NavigationControllerBase,
+ // this is the method you will typically want to call.
+ TestNavigationEntry* activeEntry() const;
+
+ // Returns the index from which we would go back/forward or reload. This is
+ // the m_lastCommittedEntryIndex if m_pendingEntryIndex is -1. Otherwise,
+ // it is the m_pendingEntryIndex.
+ int currentEntryIndex() const;
+
+ // Returns the entry at the specified index. Returns 0 if out of
+ // bounds.
+ TestNavigationEntry* entryAtIndex(int) const;
+
+ // Return the entry with the corresponding type and page ID, or 0 if
+ // not found.
+ TestNavigationEntry* entryWithPageID(int32_t) const;
+
+ // Returns the index of the last committed entry.
+ int lastCommittedEntryIndex() const { return m_lastCommittedEntryIndex; }
+
+ // Used to inform us of a navigation being committed for a tab. We will take
+ // ownership of the entry. Any entry located forward to the current entry will
+ // be deleted. The new entry becomes the current entry.
+ void didNavigateToEntry(TestNavigationEntry*);
+
+ // Used to inform us to discard its pending entry.
+ void discardPendingEntry();
+
+private:
+ // Inserts an entry after the current position, removing all entries after it.
+ // The new entry will become the active one.
+ void insertEntry(TestNavigationEntry*);
+
+ int maxPageID() const { return m_maxPageID; }
+ void navigateToPendingEntry(bool reload);
+
+ // Return the index of the entry with the corresponding type and page ID,
+ // or -1 if not found.
+ int entryIndexWithPageID(int32_t) const;
+
+ // Updates the max page ID with that of the given entry, if is larger.
+ void updateMaxPageID();
+
+ // List of NavigationEntry for this tab
+ typedef Vector<linked_ptr<TestNavigationEntry> > NavigationEntryList;
+ typedef NavigationEntryList::iterator NavigationEntryListIterator;
+ NavigationEntryList m_entries;
+
+ // An entry we haven't gotten a response for yet. This will be discarded
+ // when we navigate again. It's used only so we know what the currently
+ // displayed tab is.
+ TestNavigationEntry* m_pendingEntry;
+
+ // currently visible entry
+ int m_lastCommittedEntryIndex;
+
+ // index of pending entry if it is in entries_, or -1 if pending_entry_ is a
+ // new entry (created by LoadURL).
+ int m_pendingEntryIndex;
+
+ NavigationHost* m_host;
+ int m_maxPageID;
+};
+
+#endif // TestNavigationController_h
+
diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.cpp b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp
new file mode 100644
index 0000000..d2bc110
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestShell.h"
+
+#include "LayoutTestController.h"
+#include "WebViewHost.h"
+#include "base/md5.h" // FIXME: Wrap by webkit_support.
+#include "base/string16.h"
+#include "gfx/codec/png_codec.h" // FIXME: Remove dependecy. WebCore/platform/image-encoder is better?
+#include "net/base/escape.h" // FIXME: Remove dependency.
+#include "public/WebDataSource.h"
+#include "public/WebDocument.h"
+#include "public/WebElement.h"
+#include "public/WebFrame.h"
+#include "public/WebHistoryItem.h"
+#include "public/WebRuntimeFeatures.h"
+#include "public/WebScriptController.h"
+#include "public/WebSettings.h"
+#include "public/WebSize.h"
+#include "public/WebString.h"
+#include "public/WebURLRequest.h"
+#include "public/WebURLResponse.h"
+#include "public/WebView.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/platform_canvas.h"
+#include "webkit/support/webkit_support.h"
+#include <algorithm>
+#include <cctype>
+#include <vector>
+
+using namespace WebKit;
+using namespace std;
+
+// Content area size for newly created windows.
+static const int testWindowWidth = 800;
+static const int testWindowHeight = 600;
+
+// The W3C SVG layout tests use a different size than the other layout tests.
+static const int SVGTestWindowWidth = 480;
+static const int SVGTestWindowHeight = 360;
+
+static const char layoutTestsPattern[] = "/LayoutTests/";
+static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
+static const char fileUrlPattern[] = "file:/";
+static const char fileTestPrefix[] = "(file test):";
+static const char dataUrlPattern[] = "data:";
+static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
+
+TestShell::TestShell()
+ : m_testIsPending(false)
+ , m_testIsPreparing(false)
+ , m_focusedWidget(0)
+{
+ m_accessibilityController.set(new AccessibilityController(this));
+ m_layoutTestController.set(new LayoutTestController(this));
+ m_eventSender.set(new EventSender(this));
+ m_plainTextController.set(new PlainTextController());
+ m_textInputController.set(new TextInputController(this));
+
+ m_webViewHost = createWebView();
+ m_webView = m_webViewHost->webView();
+}
+
+TestShell::~TestShell()
+{
+ loadURL(GURL("about:blank"));
+ // Call GC twice to clean up garbage.
+ callJSGC();
+ callJSGC();
+
+ // Destroy the WebView before its WebViewHost.
+ m_webView->close();
+}
+
+void TestShell::resetWebSettings(WebView& webView)
+{
+ // Match the settings used by Mac DumpRenderTree, with the exception of
+ // fonts.
+ WebSettings* settings = webView.settings();
+#if OS(MAC_OS_X)
+ WebString serif = WebString::fromUTF8("Times");
+ settings->setCursiveFontFamily(WebString::fromUTF8("Apple Chancery"));
+ settings->setFantasyFontFamily(WebString::fromUTF8("Papyrus"));
+#else
+ // NOTE: case matters here, this must be 'times new roman', else
+ // some layout tests fail.
+ WebString serif = WebString::fromUTF8("times new roman");
+
+ // These two fonts are picked from the intersection of
+ // Win XP font list and Vista font list :
+ // http://www.microsoft.com/typography/fonts/winxp.htm
+ // http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx
+ // Some of them are installed only with CJK and complex script
+ // support enabled on Windows XP and are out of consideration here.
+ // (although we enabled both on our buildbots.)
+ // They (especially Impact for fantasy) are not typical cursive
+ // and fantasy fonts, but it should not matter for layout tests
+ // as long as they're available.
+ settings->setCursiveFontFamily(WebString::fromUTF8("Comic Sans MS"));
+ settings->setFantasyFontFamily(WebString::fromUTF8("Impact"));
+#endif
+ settings->setSerifFontFamily(serif);
+ settings->setStandardFontFamily(serif);
+ settings->setFixedFontFamily(WebString::fromUTF8("Courier"));
+
+ settings->setDefaultTextEncodingName(WebString::fromUTF8("ISO-8859-1"));
+ settings->setDefaultFontSize(16);
+ settings->setDefaultFixedFontSize(13);
+ settings->setMinimumFontSize(1);
+ settings->setMinimumLogicalFontSize(9);
+ settings->setJavaScriptCanOpenWindowsAutomatically(true);
+ settings->setDOMPasteAllowed(true);
+ settings->setDeveloperExtrasEnabled(false);
+ settings->setNeedsSiteSpecificQuirks(true);
+ settings->setShrinksStandaloneImagesToFit(false);
+ settings->setUsesEncodingDetector(false);
+ settings->setTextAreasAreResizable(false);
+ settings->setJavaEnabled(false);
+ settings->setAllowScriptsToCloseWindows(false);
+ settings->setXSSAuditorEnabled(false);
+ settings->setDownloadableBinaryFontsEnabled(true);
+ settings->setLocalStorageEnabled(true);
+ settings->setOfflineWebApplicationCacheEnabled(true);
+ settings->setAllowFileAccessFromFileURLs(true);
+
+ // LayoutTests were written with Safari Mac in mind which does not allow
+ // tabbing to links by default.
+ webView.setTabsToLinks(false);
+
+ // Allow those layout tests running as local files, i.e. under
+ // LayoutTests/http/tests/local, to access http server.
+ settings->setAllowUniversalAccessFromFileURLs(true);
+
+ settings->setJavaScriptEnabled(true);
+ settings->setPluginsEnabled(true);
+ settings->setWebSecurityEnabled(true);
+ settings->setEditableLinkBehaviorNeverLive();
+ settings->setFontRenderingModeNormal();
+ settings->setShouldPaintCustomScrollbars(true);
+ settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded();
+
+ settings->setLoadsImagesAutomatically(true);
+ settings->setImagesEnabled(true);
+}
+
+void TestShell::runFileTest(const TestParams& params)
+{
+ m_testIsPreparing = true;
+ m_params = params;
+ string testUrl = m_params.testUrl.spec();
+
+ bool inspectorTestMode = testUrl.find("/inspector/") != string::npos
+ || testUrl.find("\\inspector\\") != string::npos;
+ m_webView->settings()->setDeveloperExtrasEnabled(inspectorTestMode);
+ loadURL(m_params.testUrl);
+
+ m_testIsPreparing = false;
+ waitTestFinished();
+}
+
+static inline bool isSVGTestURL(const WebURL& url)
+{
+ return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos;
+}
+
+void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url)
+{
+ int width, height;
+ if (isSVGTestURL(url)) {
+ width = SVGTestWindowWidth;
+ height = SVGTestWindowHeight;
+ } else {
+ width = testWindowWidth;
+ height = testWindowHeight;
+ }
+ window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2));
+}
+
+void TestShell::resetTestController()
+{
+ m_accessibilityController->reset();
+ m_layoutTestController->reset();
+ m_eventSender->reset();
+ m_webViewHost->reset();
+}
+
+void TestShell::loadURL(const WebURL& url)
+{
+ m_webViewHost->loadURLForFrame(url, WebString());
+}
+
+void TestShell::reload()
+{
+ m_webViewHost->navigationController()->reload();
+}
+
+void TestShell::goToOffset(int offset)
+{
+ m_webViewHost->navigationController()->goToOffset(offset);
+}
+
+int TestShell::navigationEntryCount() const
+{
+ return m_webViewHost->navigationController()->entryCount();
+}
+
+void TestShell::callJSGC()
+{
+ m_webView->mainFrame()->collectGarbage();
+}
+
+void TestShell::setFocus(WebWidget* widget, bool enable)
+{
+ // Simulate the effects of InteractiveSetFocus(), which includes calling
+ // both setFocus() and setIsActive().
+ if (enable) {
+ if (m_focusedWidget != widget) {
+ if (m_focusedWidget)
+ m_focusedWidget->setFocus(false);
+ webView()->setIsActive(enable);
+ widget->setFocus(enable);
+ m_focusedWidget = widget;
+ }
+ } else {
+ if (m_focusedWidget == widget) {
+ widget->setFocus(enable);
+ webView()->setIsActive(enable);
+ m_focusedWidget = 0;
+ }
+ }
+}
+
+void TestShell::testFinished()
+{
+ if (!m_testIsPending)
+ return;
+ m_testIsPending = false;
+ dump();
+ webkit_support::QuitMessageLoop();
+}
+
+void TestShell::testTimedOut()
+{
+ fprintf(stderr, "FAIL: Timed out waiting for notifyDone to be called\n");
+ fprintf(stdout, "FAIL: Timed out waiting for notifyDone to be called\n");
+ testFinished();
+}
+
+static string dumpDocumentText(WebFrame* frame)
+{
+ // We use the document element's text instead of the body text here because
+ // not all documents have a body, such as XML documents.
+ WebElement documentElement = frame->document().documentElement();
+ if (documentElement.isNull())
+ return string();
+ return documentElement.innerText().utf8();
+}
+
+static string dumpFramesAsText(WebFrame* frame, bool recursive)
+{
+ string result;
+
+ // Add header for all but the main frame. Skip empty frames.
+ if (frame->parent() && !frame->document().documentElement().isNull()) {
+ result.append("\n--------\nFrame: '");
+ result.append(frame->name().utf8().data());
+ result.append("'\n--------\n");
+ }
+
+ result.append(dumpDocumentText(frame));
+ result.append("\n");
+
+ if (recursive) {
+ for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
+ result.append(dumpFramesAsText(child, recursive));
+ }
+
+ return result;
+}
+
+static void dumpFrameScrollPosition(WebFrame* frame, bool recursive)
+{
+ WebSize offset = frame->scrollOffset();
+ if (offset.width > 0 || offset.height > 0) {
+ if (frame->parent())
+ printf("frame '%s' ", frame->name().utf8().data());
+ printf("scrolled to %d,%d\n", offset.width, offset.height);
+ }
+
+ if (!recursive)
+ return;
+ for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
+ dumpFrameScrollPosition(child, recursive);
+}
+
+struct ToLower {
+ char16 operator()(char16 c) { return tolower(c); }
+};
+
+// FIXME: Eliminate std::transform(), std::vector, and std::sort().
+
+// Returns True if item1 < item2.
+static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
+{
+ string16 target1 = item1.target();
+ string16 target2 = item2.target();
+ std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
+ std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
+ return target1 < target2;
+}
+
+static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
+{
+ string result;
+
+ if (isCurrent) {
+ result.append("curr->");
+ result.append(indent - 6, ' '); // 6 == "curr->".length()
+ } else {
+ result.append(indent, ' ');
+ }
+
+ string url = item.urlString().utf8();
+ size_t pos;
+ if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) {
+ // adjust file URLs to match upstream results.
+ url.replace(0, pos + layoutTestsPatternSize, fileTestPrefix);
+ } else if (!url.find(dataUrlPattern)) {
+ // URL-escape data URLs to match results upstream.
+ string path = EscapePath(url.substr(dataUrlPatternSize));
+ url.replace(dataUrlPatternSize, url.length(), path);
+ }
+
+ result.append(url);
+ if (!item.target().isEmpty()) {
+ result.append(" (in frame \"");
+ result.append(item.target().utf8());
+ result.append("\")");
+ }
+ if (item.isTargetItem())
+ result.append(" **nav target**");
+ result.append("\n");
+
+ const WebVector<WebHistoryItem>& children = item.children();
+ if (!children.isEmpty()) {
+ // Must sort to eliminate arbitrary result ordering which defeats
+ // reproducible testing.
+ // FIXME: WebVector should probably just be a std::vector!!
+ std::vector<WebHistoryItem> sortedChildren;
+ for (size_t i = 0; i < children.size(); ++i)
+ sortedChildren.push_back(children[i]);
+ std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
+ for (size_t i = 0; i < sortedChildren.size(); ++i)
+ result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
+ }
+
+ return result;
+}
+
+static void dumpBackForwardList(const TestNavigationController& navigationController, string& result)
+{
+ result.append("\n============== Back Forward List ==============\n");
+ for (int index = 0; index < navigationController.entryCount(); ++index) {
+ int currentIndex = navigationController.lastCommittedEntryIndex();
+ WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
+ if (historyItem.isNull()) {
+ historyItem.initialize();
+ historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
+ }
+ result.append(dumpHistoryItem(historyItem, 8, index == currentIndex));
+ }
+ result.append("===============================================\n");
+}
+
+string TestShell::dumpAllBackForwardLists()
+{
+ string result;
+ for (unsigned i = 0; i < m_windowList.size(); ++i)
+ dumpBackForwardList(*m_windowList[i]->navigationController(), result);
+ return result;
+}
+
+void TestShell::dump()
+{
+ WebScriptController::flushConsoleMessages();
+
+ // Dump the requested representation.
+ WebFrame* frame = m_webView->mainFrame();
+ if (!frame)
+ return;
+ bool shouldDumpAsText = m_layoutTestController->shouldDumpAsText();
+ bool dumpedAnything = false;
+ if (m_params.dumpTree) {
+ dumpedAnything = true;
+ printf("Content-Type: text/plain\n");
+ // Text output: the test page can request different types of output
+ // which we handle here.
+ if (!shouldDumpAsText) {
+ // Plain text pages should be dumped as text
+ string mimeType = frame->dataSource()->response().mimeType().utf8();
+ shouldDumpAsText = mimeType == "text/plain";
+ }
+ if (shouldDumpAsText) {
+ bool recursive = m_layoutTestController->shouldDumpChildFramesAsText();
+ string dataUtf8 = dumpFramesAsText(frame, recursive);
+ if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
+ FATAL("Short write to stdout, disk full?\n");
+ } else {
+ printf("%s", frame->renderTreeAsText().utf8().data());
+ bool recursive = m_layoutTestController->shouldDumpChildFrameScrollPositions();
+ dumpFrameScrollPosition(frame, recursive);
+ }
+ if (m_layoutTestController->shouldDumpBackForwardList())
+ printf("%s", dumpAllBackForwardLists().c_str());
+ }
+ if (dumpedAnything && m_params.printSeparators)
+ printf("#EOF\n");
+
+ if (m_params.dumpPixels && !shouldDumpAsText) {
+ // Image output: we write the image data to the file given on the
+ // command line (for the dump pixels argument), and the MD5 sum to
+ // stdout.
+ dumpedAnything = true;
+ m_webView->layout();
+ if (m_layoutTestController->testRepaint()) {
+ WebSize viewSize = m_webView->size();
+ int width = viewSize.width;
+ int height = viewSize.height;
+ if (m_layoutTestController->sweepHorizontally()) {
+ for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
+ m_webViewHost->paintRect(column);
+ } else {
+ for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
+ m_webViewHost->paintRect(line);
+ }
+ } else
+ m_webViewHost->paintInvalidatedRegion();
+
+ // See if we need to draw the selection bounds rect. Selection bounds
+ // rect is the rect enclosing the (possibly transformed) selection.
+ // The rect should be drawn after everything is laid out and painted.
+ if (m_layoutTestController->shouldDumpSelectionRect()) {
+ // If there is a selection rect - draw a red 1px border enclosing rect
+ WebRect wr = frame->selectionBoundsRect();
+ if (!wr.isEmpty()) {
+ // Render a red rectangle bounding selection rect
+ SkPaint paint;
+ paint.setColor(0xFFFF0000); // Fully opaque red
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setStrokeWidth(1.0f);
+ SkIRect rect; // Bounding rect
+ rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
+ m_webViewHost->canvas()->drawIRect(rect, paint);
+ }
+ }
+
+ string md5sum = dumpImage(m_webViewHost->canvas(), m_params.pixelHash);
+ }
+ printf("#EOF\n"); // For the image.
+ fflush(stdout);
+ fflush(stderr);
+}
+
+string TestShell::dumpImage(skia::PlatformCanvas* canvas, const string& expectedHash)
+{
+ skia::BitmapPlatformDevice& device =
+ static_cast<skia::BitmapPlatformDevice&>(canvas->getTopPlatformDevice());
+ const SkBitmap& sourceBitmap = device.accessBitmap(false);
+
+ SkAutoLockPixels sourceBitmapLock(sourceBitmap);
+
+ // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
+ // to keep it. On Windows, the alpha channel is wrong since text/form control
+ // drawing may have erased it in a few places. So on Windows we force it to
+ // opaque and also don't write the alpha channel for the reference. Linux
+ // doesn't have the wrong alpha like Windows, but we ignore it anyway.
+#if OS(WINDOWS)
+ bool discardTransparency = true;
+ device.makeOpaque(0, 0, sourceBitmap.width(), sourceBitmap.height());
+#elif OS(MAC_OS_X)
+ bool discardTransparency = false;
+#elif OS(UNIX)
+ bool discardTransparency = true;
+#endif
+
+ // Compute MD5 sum. We should have done this before calling
+ // device.makeOpaque on Windows. Because we do it after the call, there are
+ // some images that are the pixel identical on windows and other platforms
+ // but have different MD5 sums. At this point, rebaselining all the windows
+ // tests is too much of a pain, so we just check in different baselines.
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, sourceBitmap.getPixels(), sourceBitmap.getSize());
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+ string md5hash = MD5DigestToBase16(digest);
+ printf("\nActualHash: %s\n", md5hash.c_str());
+ if (!expectedHash.empty())
+ printf("\nExpectedHash: %s\n", expectedHash.c_str());
+
+ // Only encode and dump the png if the hashes don't match. Encoding the image
+ // is really expensive.
+ if (md5hash.compare(expectedHash)) {
+ std::vector<unsigned char> png;
+ gfx::PNGCodec::ColorFormat colorFormat = gfx::PNGCodec::FORMAT_BGRA;
+ gfx::PNGCodec::Encode(
+ reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()),
+ colorFormat, sourceBitmap.width(), sourceBitmap.height(),
+ static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, &png);
+
+ printf("Content-Type: image/png\n");
+ printf("Content-Length: %u\n", png.size());
+ // Write to disk.
+ if (fwrite(&png[0], 1, png.size(), stdout) != png.size())
+ FATAL("Short write to stdout.\n");
+ }
+
+ return md5hash;
+}
+
+void TestShell::bindJSObjectsToWindow(WebFrame* frame)
+{
+ m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController"));
+ m_layoutTestController->bindToJavascript(frame, WebString::fromUTF8("layoutTestController"));
+ m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender"));
+ m_plainTextController->bindToJavascript(frame, WebString::fromUTF8("plainText"));
+ m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController"));
+}
+
+int TestShell::layoutTestTimeout()
+{
+ return 10 * 1000;
+}
+
+WebViewHost* TestShell::createWebView()
+{
+ return createNewWindow(WebURL());
+}
+
+WebViewHost* TestShell::createNewWindow(const WebURL& url)
+{
+ WebViewHost* host = new WebViewHost(this);
+ WebView* view = WebView::create(host);
+ host->setWebWidget(view);
+ resetWebSettings(*view);
+ view->initializeMainFrame(host);
+ m_windowList.append(host);
+ host->loadURLForFrame(url, WebString());
+ return host;
+}
+
+void TestShell::closeWindow(WebViewHost* window)
+{
+ size_t i = m_windowList.find(window);
+ if (i == notFound) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ m_windowList.remove(i);
+ window->webWidget()->close();
+ delete window;
+}
+
+void TestShell::closeRemainingWindows()
+{
+ // Iterate through the window list and close everything except the main
+ // ihwindow. We don't want to delete elements as we're iterating, so we copy
+ // to a temp vector first.
+ Vector<WebViewHost*> windowsToDelete;
+ for (unsigned i = 0; i < m_windowList.size(); ++i) {
+ if (m_windowList[i] != webViewHost())
+ windowsToDelete.append(m_windowList[i]);
+ }
+ ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
+ for (unsigned i = 0; i < windowsToDelete.size(); ++i)
+ closeWindow(windowsToDelete[i]);
+ ASSERT(m_windowList.size() == 1);
+}
+
+int TestShell::windowCount()
+{
+ return m_windowList.size();
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.h b/WebKitTools/DumpRenderTree/chromium/TestShell.h
new file mode 100644
index 0000000..c6a5b2e
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestShell.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "AccessibilityController.h"
+#include "EventSender.h"
+#include "LayoutTestController.h"
+#include "PlainTextController.h"
+#include "TextInputController.h"
+#include "WebViewHost.h"
+#include <string>
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+
+// TestShell is a container of global variables and has bridge functions between
+// various objects. Only one instance is created in one DRT process.
+
+namespace WebKit {
+class WebFrame;
+class WebPreferences;
+class WebView;
+class WebURL;
+}
+namespace skia {
+class PlatformCanvas;
+}
+
+struct TestParams {
+ bool dumpTree;
+ bool dumpPixels;
+ bool printSeparators;
+ WebKit::WebURL testUrl;
+ std::string pixelFileName;
+ std::string pixelHash;
+
+ TestParams()
+ : dumpTree(true)
+ , dumpPixels(false)
+ , printSeparators(false) {}
+};
+
+class TestShell {
+public:
+ TestShell();
+ ~TestShell();
+ // The main WebView.
+ WebKit::WebView* webView() const { return m_webView; }
+ // Returns the host for the main WebView.
+ WebViewHost* webViewHost() const { return m_webViewHost; }
+ LayoutTestController* layoutTestController() const { return m_layoutTestController.get(); }
+ AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); }
+
+ void bindJSObjectsToWindow(WebKit::WebFrame*);
+ void runFileTest(const TestParams&);
+ void callJSGC();
+ void resetTestController();
+ void waitTestFinished();
+
+ // Operations to the main window.
+ void loadURL(const WebKit::WebURL& url);
+ void reload();
+ void goToOffset(int offset);
+ int navigationEntryCount() const;
+
+ void setFocus(WebKit::WebWidget*, bool enable);
+ bool shouldDumpFrameLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpFrameLoadCallbacks(); }
+ bool shouldDumpResourceLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpResourceLoadCallbacks(); }
+ void setIsLoading(bool flag) { m_isLoading = flag; }
+
+ // Called by the LayoutTestController to signal test completion.
+ void testFinished();
+ // Called by LayoutTestController when a test hits the timeout, but does not
+ // cause a hang. We can avoid killing TestShell in this case and still dump
+ // the test results.
+ void testTimedOut();
+
+#if defined(OS_WIN)
+ // Access to the finished event. Used by the static WatchDog thread.
+ HANDLE finishedEvent() { return m_finishedEvent; }
+#endif
+
+ // Get the timeout for running a test in milliseconds.
+ static int layoutTestTimeout();
+ static int layoutTestTimeoutForWatchDog() { return layoutTestTimeout() + 1000; }
+
+ WebViewHost* createWebView();
+ WebViewHost* createNewWindow(const WebKit::WebURL&);
+ void closeWindow(WebViewHost*);
+ void closeRemainingWindows();
+ int windowCount();
+ static void resizeWindowForTest(WebViewHost*, const WebKit::WebURL&);
+
+ static const int virtualWindowBorder = 3;
+
+private:
+ static void resetWebSettings(WebKit::WebView&);
+ void dump();
+ std::string dumpAllBackForwardLists();
+ static std::string dumpImage(skia::PlatformCanvas*, const std::string& expectedHash);
+
+ bool m_testIsPending;
+ bool m_testIsPreparing;
+ bool m_isLoading;
+ WebKit::WebView* m_webView;
+ WebKit::WebWidget* m_focusedWidget;
+ WebViewHost* m_webViewHost;
+ OwnPtr<AccessibilityController*> m_accessibilityController;
+ OwnPtr<EventSender*> m_eventSender;
+ OwnPtr<LayoutTestController*> m_layoutTestController;
+ OwnPtr<PlainTextController*> m_plainTextController;
+ OwnPtr<TextInputController*> m_textInputController;
+ TestParams m_params;
+
+ // List of all windows in this process.
+ // The main window should be put into windowList[0].
+ typedef Vector<WebViewHost*> WindowList;
+ WindowList m_windowList;
+
+#if defined(OS_WIN)
+ // Used by the watchdog to know when it's finished.
+ HANDLE m_finishedEvent;
+#endif
+};
diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp b/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp
new file mode 100644
index 0000000..d71881a
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestShell.h"
+
+#include "webkit/support/webkit_support.h"
+#include <signal.h>
+
+static void AlarmHandler(int signatl)
+{
+ // If the alarm alarmed, kill the process since we have a really bad hang.
+ puts("\n#TEST_TIMED_OUT\n");
+ puts("#EOF\n");
+ fflush(stdout);
+ exit(0);
+}
+
+void TestShell::waitTestFinished()
+{
+ ASSERT(!m_testIsPending);
+
+ m_testIsPending = true;
+
+ // Install an alarm signal handler that will kill us if we time out.
+ signal(SIGALRM, AlarmHandler);
+ alarm(layoutTestTimeoutForWatchDog() / 1000);
+
+ // TestFinished() will post a quit message to break this loop when the page
+ // finishes loading.
+ while (m_testIsPending)
+ webkit_support::RunMessageLoop();
+
+ // Remove the alarm.
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm b/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm
new file mode 100644
index 0000000..ec8dbac
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "TestShell.h"
+#include "webkit/support/webkit_support.h"
+#import <AppKit/AppKit.h>
+
+// A class to be the target/selector of the "watchdog" thread that ensures
+// pages timeout if they take too long and tells the test harness via stdout.
+@interface WatchDogTarget : NSObject {
+@private
+ NSTimeInterval _timeout;
+}
+// |timeout| is in seconds
+- (id)initWithTimeout:(NSTimeInterval)timeout;
+// serves as the "run" method of a NSThread.
+- (void)run:(id)sender;
+@end
+
+@implementation WatchDogTarget
+
+- (id)initWithTimeout:(NSTimeInterval)timeout
+{
+ if ((self = [super init]))
+ _timeout = timeout;
+ return self;
+}
+
+- (void)run:(id)ignore
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ // check for debugger, just bail if so. We don't want the timeouts hitting
+ // when we're trying to track down an issue.
+ if (webkit_support::BeingDebugged())
+ return;
+
+ NSThread* currentThread = [NSThread currentThread];
+
+ // Wait to be cancelled. If we are that means the test finished. If it hasn't,
+ // then we need to tell the layout script we timed out and start again.
+ NSDate* limitDate = [NSDate dateWithTimeIntervalSinceNow:_timeout];
+ while ([(NSDate*)[NSDate date] compare:limitDate] == NSOrderedAscending &&
+ ![currentThread isCancelled]) {
+ // sleep for a small increment then check again
+ NSDate* incrementDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
+ [NSThread sleepUntilDate:incrementDate];
+ }
+ if (![currentThread isCancelled]) {
+ // Print a warning to be caught by the layout-test script.
+ // Note: the layout test driver may or may not recognize
+ // this as a timeout.
+ puts("#TEST_TIMED_OUT\n");
+ puts("#EOF\n");
+ fflush(stdout);
+ exit(0);
+ }
+
+ [pool release];
+}
+
+@end
+
+void TestShell::waitTestFinished()
+{
+ ASSERT(!m_testIsPending);
+
+ m_testIsPending = true;
+
+ // Create a watchdog thread which just sets a timer and
+ // kills the process if it times out. This catches really
+ // bad hangs where the shell isn't coming back to the
+ // message loop. If the watchdog is what catches a
+ // timeout, it can't do anything except terminate the test
+ // shell, which is unfortunate.
+ // Windows multiplies by 2.5, but that causes us to run for far, far too
+ // long. We use the passed value and let the scripts flag override
+ // the value as needed.
+ NSTimeInterval timeoutSeconds = layoutTestTimeoutForWatchDog() / 1000;
+ WatchDogTarget* watchdog = [[[WatchDogTarget alloc]
+ initWithTimeout:timeoutSeconds] autorelease];
+ NSThread* thread = [[NSThread alloc] initWithTarget:watchdog
+ selector:@selector(run:)
+ object:nil];
+ [thread start];
+
+ // TestFinished() will post a quit message to break this loop when the page
+ // finishes loading.
+ while (m_testIsPending)
+ webkit_support::RunMessageLoop();
+
+ // Tell the watchdog that we're finished. No point waiting to re-join, it'll
+ // die on its own.
+ [thread cancel];
+ [thread release];
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp b/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp
new file mode 100644
index 0000000..2d806a2
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestShell.h"
+
+#include "webkit/support/webkit_support.h"
+#include <process.h>
+
+// Default timeout in ms for file page loads when in layout test mode.
+const int kDefaultFileTestTimeoutMillisecs = 10 * 1000;
+const int kDefaultWatchDogTimeoutMillisecs = kDefaultFileTestTimeoutMillisecs + 1 * 1000;
+
+// Thread main to run for the thread which just tests for timeout.
+unsigned int __stdcall watchDogThread(void *arg)
+{
+ // If we're debugging a layout test, don't timeout.
+ if (::IsDebuggerPresent())
+ return 0;
+
+ TestShell* shell = static_cast<TestShell*>(arg);
+ // FIXME: Do we need user-specified time settings as with the original
+ // Chromium implementation?
+ DWORD timeout = static_cast<DWORD>(kDefaultWatchDogTimeoutMillisecs);
+ DWORD rv = WaitForSingleObject(shell->finishedEvent(), timeout);
+ if (rv == WAIT_TIMEOUT) {
+ // Print a warning to be caught by the layout-test script.
+ // Note: the layout test driver may or may not recognize
+ // this as a timeout.
+ puts("#TEST_TIMED_OUT\n");
+ puts("#EOF\n");
+ fflush(stdout);
+ TerminateProcess(GetCurrentProcess(), 0);
+ }
+ // Finished normally.
+ return 0;
+}
+
+void TestShell::waitTestFinished()
+{
+ DCHECK(!m_testIsPending) << "cannot be used recursively";
+
+ m_testIsPending = true;
+
+ // Create a watchdog thread which just sets a timer and
+ // kills the process if it times out. This catches really
+ // bad hangs where the shell isn't coming back to the
+ // message loop. If the watchdog is what catches a
+ // timeout, it can't do anything except terminate the test
+ // shell, which is unfortunate.
+ m_finishedEvent = CreateEvent(0, TRUE, FALSE, 0);
+ DCHECK(m_finishedEvent);
+
+ HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(
+ 0,
+ 0,
+ &watchDogThread,
+ this,
+ 0,
+ 0));
+ DCHECK(threadHandle);
+
+ // TestFinished() will post a quit message to break this loop when the page
+ // finishes loading.
+ while (m_testIsPending)
+ webkit_support::RunMessageLoop();
+
+ // Tell the watchdog that we are finished.
+ SetEvent(m_finishedEvent);
+
+ // Wait to join the watchdog thread. (up to 1s, then quit)
+ WaitForSingleObject(threadHandle, 1000);
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h
new file mode 100644
index 0000000..899514e
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestWebWorker_h
+#define TestWebWorker_h
+
+#include "public/WebMessagePortChannel.h"
+#include "public/WebWorker.h"
+#include "public/WebWorkerClient.h"
+#include <wtf/RefCounted.h>
+
+namespace WebKit {
+class WebNotificationPresenter;
+class WebString;
+class WebURL;
+}
+
+class TestWebWorker : public WebKit::WebWorker,
+ public WebKit::WebWorkerClient,
+ public WTF::RefCounted<TestWebWorker> {
+public:
+ TestWebWorker()
+ {
+ ref();
+ // The initial counter value should be 2. One for a worker object,
+ // another for a worker context object. We need to call ref() just once
+ // because the default counter value of RefCounted is 1.
+ }
+
+ // WebWorker methods:
+ virtual void startWorkerContext(const WebKit::WebURL&, const WebKit::WebString&, const WebKit::WebString&) {}
+ virtual void terminateWorkerContext() {}
+ virtual void postMessageToWorkerContext(const WebKit::WebString&, const WebKit::WebMessagePortChannelArray&) {}
+ virtual void workerObjectDestroyed()
+ {
+ // Releases the reference held for worker object.
+ deref();
+ }
+ virtual void clientDestroyed() {}
+
+ // WebWorkerClient methods:
+ virtual void postMessageToWorkerObject(const WebKit::WebString&, const WebKit::WebMessagePortChannelArray&) {}
+ virtual void postExceptionToWorkerObject(const WebKit::WebString&, int, const WebKit::WebString&) {}
+ virtual void postConsoleMessageToWorkerObject(int, int, int, int, const WebKit::WebString&, int, const WebKit::WebString&) {}
+ virtual void confirmMessageFromWorkerObject(bool) {}
+ virtual void reportPendingActivity(bool) {}
+ virtual void workerContextClosed() {}
+ virtual void workerContextDestroyed()
+ {
+ // Releases the reference held for worker context object.
+ deref();
+ }
+ virtual WebKit::WebWorker* createWorker(WebKit::WebWorkerClient*) { return 0; }
+ virtual WebKit::WebNotificationPresenter* notificationPresenter() { return 0; }
+
+private:
+ ~TestWebWorker() {}
+ friend class WTF::RefCounted<TestWebWorker>;
+};
+
+#endif // TestWebWorker_h
diff --git a/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp b/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp
new file mode 100644
index 0000000..63c4719
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextInputController.h"
+
+#include "TestShell.h"
+#include "public/WebFrame.h"
+#include "public/WebRange.h"
+#include "public/WebString.h"
+#include "public/WebView.h"
+#include <wtf/StringExtras.h>
+#include <string>
+
+using namespace WebKit;
+using namespace std;
+
+TestShell* TextInputController::testShell = 0;
+
+TextInputController::TextInputController(TestShell* shell)
+{
+ // Set static testShell variable. Be careful not to assign testShell to new
+ // windows which are temporary.
+ if (!testShell)
+ testShell = shell;
+
+ bindMethod("insertText", &TextInputController::insertText);
+ bindMethod("doCommand", &TextInputController::doCommand);
+ bindMethod("setMarkedText", &TextInputController::setMarkedText);
+ bindMethod("unmarkText", &TextInputController::unmarkText);
+ bindMethod("hasMarkedText", &TextInputController::hasMarkedText);
+ bindMethod("conversationIdentifier", &TextInputController::conversationIdentifier);
+ bindMethod("substringFromRange", &TextInputController::substringFromRange);
+ bindMethod("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange);
+ bindMethod("markedRange", &TextInputController::markedRange);
+ bindMethod("selectedRange", &TextInputController::selectedRange);
+ bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange);
+ bindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint);
+ bindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText);
+ bindMethod("makeAttributedString", &TextInputController::makeAttributedString);
+}
+
+WebFrame* TextInputController::getMainFrame()
+{
+ return testShell->webView()->mainFrame();
+}
+
+void TextInputController::insertText(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+ if (arguments.size() < 1 || !arguments[0].isString())
+ return;
+
+ if (mainFrame->hasMarkedText()) {
+ mainFrame->unmarkText();
+ mainFrame->replaceSelection(WebString());
+ }
+ mainFrame->insertText(WebString::fromUTF8(arguments[0].toString()));
+}
+
+void TextInputController::doCommand(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ if (arguments.size() >= 1 && arguments[0].isString())
+ mainFrame->executeCommand(WebString::fromUTF8(arguments[0].toString()));
+}
+
+void TextInputController::setMarkedText(const CppArgumentList& arguments, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ if (arguments.size() >= 3 && arguments[0].isString()
+ && arguments[1].isNumber() && arguments[2].isNumber()) {
+ mainFrame->setMarkedText(WebString::fromUTF8(arguments[0].toString()),
+ arguments[1].toInt32(),
+ arguments[2].toInt32());
+ }
+}
+
+void TextInputController::unmarkText(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ mainFrame->unmarkText();
+}
+
+void TextInputController::hasMarkedText(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ result->set(mainFrame->hasMarkedText());
+}
+
+void TextInputController::conversationIdentifier(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void TextInputController::substringFromRange(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void TextInputController::attributedSubstringFromRange(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void TextInputController::markedRange(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ WebRange range = mainFrame->markedRange();
+ char buffer[30];
+ snprintf(buffer, 30, "%d,%d", range.startOffset(), range.endOffset());
+ result->set(string(buffer));
+}
+
+void TextInputController::selectedRange(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ WebRange range = mainFrame->selectionRange();
+ char buffer[30];
+ snprintf(buffer, 30, "%d,%d", range.startOffset(), range.endOffset());
+ result->set(string(buffer));
+}
+
+void TextInputController::firstRectForCharacterRange(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void TextInputController::characterIndexForPoint(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
+
+void TextInputController::validAttributesForMarkedText(const CppArgumentList&, CppVariant* result)
+{
+ result->setNull();
+
+ WebFrame* mainFrame = getMainFrame();
+ if (!mainFrame)
+ return;
+
+ result->set("NSUnderline,NSUnderlineColor,NSMarkedClauseSegment,"
+ "NSTextInputReplacementRangeAttributeName");
+}
+
+void TextInputController::makeAttributedString(const CppArgumentList&, CppVariant* result)
+{
+ // FIXME: Implement this.
+ result->setNull();
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/TextInputController.h b/WebKitTools/DumpRenderTree/chromium/TextInputController.h
new file mode 100644
index 0000000..a912ba1
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/TextInputController.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+<<<<<<< HEAD:WebCore/bindings/v8/custom/V8NavigatorCustom.cpp
+#include "config.h"
+#include "V8Navigator.h"
+
+#include "RuntimeEnabledFeatures.h"
+#include "V8DOMWindow.h"
+#include "V8DOMWrapper.h"
+
+#if PLATFORM(ANDROID)
+#include "ExceptionCode.h"
+#include "V8CustomApplicationInstalledCallback.h"
+#include "V8Proxy.h"
+#endif
+
+namespace WebCore {
+
+v8::Handle<v8::Value> toV8(Navigator* impl)
+{
+ if (!impl)
+ return v8::Null();
+ v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(impl);
+ if (wrapper.IsEmpty()) {
+ wrapper = V8Navigator::wrap(impl);
+ if (!wrapper.IsEmpty())
+ V8DOMWrapper::setHiddenWindowReference(impl->frame(), V8DOMWindow::navigatorIndex, wrapper);
+ }
+ return wrapper;
+}
+
+#if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
+
+static PassRefPtr<ApplicationInstalledCallback> createApplicationInstalledCallback(
+ v8::Local<v8::Value> value, bool& succeeded)
+{
+ succeeded = true;
+
+ if (!value->IsFunction()) {
+ succeeded = false;
+ throwError("The second argument should be a function");
+ return 0;
+ }
+
+ Frame* frame = V8Proxy::retrieveFrameForCurrentContext();
+ return V8CustomApplicationInstalledCallback::create(value, frame);
+}
+
+v8::Handle<v8::Value> V8Navigator::isApplicationInstalledCallback(const v8::Arguments& args)
+{
+ INC_STATS("DOM.isApplicationInstalled()");
+ bool succeeded = false;
+
+ if (args.Length() < 2)
+ return throwError("Two arguments required: an application name and a callback.", V8Proxy::SyntaxError);
+
+ if (!args[0]->IsString())
+ return throwError("The first argument should be a string.");
+
+ RefPtr<ApplicationInstalledCallback> callback =
+ createApplicationInstalledCallback(args[1], succeeded);
+ if (!succeeded)
+ return v8::Undefined();
+
+ ASSERT(callback);
+
+ Navigator* navigator = V8Navigator::toNative(args.Holder());
+ if (!navigator->isApplicationInstalled(toWebCoreString(args[0]), callback.release()))
+ return throwError(INVALID_STATE_ERR);
+
+ return v8::Undefined();
+}
+
+#endif // PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
+
+} // namespace WebCore
+=======
+// TextInputController is bound to window.textInputController in Javascript
+// when DRT is running. Layout tests use it to exercise various corners of
+// text input.
+
+#ifndef TextInputController_h
+#define TextInputController_h
+
+#include "CppBoundClass.h"
+
+class TestShell;
+
+namespace WebKit {
+class WebFrame;
+}
+
+class TextInputController : public CppBoundClass {
+public:
+ TextInputController(TestShell*);
+
+ void insertText(const CppArgumentList&, CppVariant*);
+ void doCommand(const CppArgumentList&, CppVariant*);
+ void setMarkedText(const CppArgumentList&, CppVariant*);
+ void unmarkText(const CppArgumentList&, CppVariant*);
+ void hasMarkedText(const CppArgumentList&, CppVariant*);
+ void conversationIdentifier(const CppArgumentList&, CppVariant*);
+ void substringFromRange(const CppArgumentList&, CppVariant*);
+ void attributedSubstringFromRange(const CppArgumentList&, CppVariant*);
+ void markedRange(const CppArgumentList&, CppVariant*);
+ void selectedRange(const CppArgumentList&, CppVariant*);
+ void firstRectForCharacterRange(const CppArgumentList&, CppVariant*);
+ void characterIndexForPoint(const CppArgumentList&, CppVariant*);
+ void validAttributesForMarkedText(const CppArgumentList&, CppVariant*);
+ void makeAttributedString(const CppArgumentList&, CppVariant*);
+
+private:
+ // Returns the test shell's main WebFrame.
+ static WebKit::WebFrame* getMainFrame();
+
+ // Non-owning pointer. The TextInputController is owned by the TestShell.
+ static TestShell* testShell;
+};
+
+#endif // TextInputController_h
+>>>>>>> webkit.org at r58033:WebKitTools/DumpRenderTree/chromium/TextInputController.h
diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp
new file mode 100644
index 0000000..95b1c7e
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp
@@ -0,0 +1,1316 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebViewHost.h"
+
+#include "LayoutTestController.h"
+#include "TestNavigationController.h"
+#include "TestShell.h"
+#include "TestWebWorker.h"
+#include "net/base/net_errors.h" // FIXME: can we remove this?
+#include "public/WebCString.h"
+#include "public/WebConsoleMessage.h"
+#include "public/WebContextMenuData.h"
+#include "public/WebDataSource.h"
+#include "public/WebDragData.h"
+#include "public/WebFrame.h"
+#include "public/WebHistoryItem.h"
+#include "public/WebNode.h"
+#include "public/WebRange.h"
+#include "public/WebRect.h"
+#include "public/WebScreenInfo.h"
+#include "public/WebSize.h"
+#include "public/WebStorageNamespace.h"
+#include "public/WebURLRequest.h"
+#include "public/WebURLResponse.h"
+#include "public/WebView.h"
+#include "skia/ext/platform_canvas.h"
+#include "webkit/support/webkit_support.h"
+#include <wtf/Assertions.h>
+
+using namespace WebKit;
+using namespace skia;
+using namespace std;
+
+static const int screenWidth = 1920;
+static const int screenHeight = 1080;
+static const int screenUnavailableBorder = 8;
+
+// WebNavigationType debugging strings taken from PolicyDelegate.mm.
+static const char* linkClickedString = "link clicked";
+static const char* formSubmittedString = "form submitted";
+static const char* backForwardString = "back/forward";
+static const char* reloadString = "reload";
+static const char* formResubmittedString = "form resubmitted";
+static const char* otherString = "other";
+static const char* illegalString = "illegal value";
+
+static int nextPageID = 1;
+
+// Used to write a platform neutral file:/// URL by only taking the filename
+// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt").
+static string urlSuitableForTestResult(const string& url)
+{
+ if (url.empty() || string::npos == url.find("file://"))
+ return url;
+
+ size_t pos = url.rfind('/');
+ if (pos == string::npos) {
+#if OS(WINDOWS)
+ pos = url.rfind('\\');
+ if (pos == string::npos)
+ pos = 0;
+#else
+ pos = 0;
+#endif
+ }
+ string filename = url.substr(pos + 1);
+ if (filename.empty())
+ return "file:"; // A WebKit test has this in its expected output.
+ return filename;
+}
+
+// Used to write a platform neutral file:/// URL by taking the
+// filename and its directory. (e.g., converts
+// "file:///tmp/foo/bar.txt" to just "bar.txt").
+static string descriptionSuitableForTestResult(const string& url)
+{
+ if (url.empty() || string::npos == url.find("file://"))
+ return url;
+
+ size_t pos = url.rfind('/');
+ if (pos == string::npos || !pos)
+ return "ERROR:" + url;
+ pos = url.rfind('/', pos - 1);
+ if (pos == string::npos)
+ return "ERROR:" + url;
+
+ return url.substr(pos + 1);
+}
+
+// Adds a file called "DRTFakeFile" to |data_object| (CF_HDROP). Use to fake
+// dragging a file.
+static void addDRTFakeFileToDataObject(WebDragData* dragData)
+{
+ dragData->appendToFileNames(WebString::fromUTF8("DRTFakeFile"));
+}
+
+// Get a debugging string from a WebNavigationType.
+static const char* webNavigationTypeToString(WebNavigationType type)
+{
+ switch (type) {
+ case WebKit::WebNavigationTypeLinkClicked:
+ return linkClickedString;
+ case WebKit::WebNavigationTypeFormSubmitted:
+ return formSubmittedString;
+ case WebKit::WebNavigationTypeBackForward:
+ return backForwardString;
+ case WebKit::WebNavigationTypeReload:
+ return reloadString;
+ case WebKit::WebNavigationTypeFormResubmitted:
+ return formResubmittedString;
+ case WebKit::WebNavigationTypeOther:
+ return otherString;
+ }
+ return illegalString;
+}
+
+static string URLDescription(const GURL& url)
+{
+ if (url.SchemeIs("file"))
+ return url.ExtractFileName();
+ return url.possibly_invalid_spec();
+}
+
+static void printResponseDescription(const WebURLResponse& response)
+{
+ if (response.isNull()) {
+ fputs("(null)", stdout);
+ return;
+ }
+ string url = response.url().spec();
+ printf("<NSURLResponse %s, http status code %d>",
+ descriptionSuitableForTestResult(url).c_str(),
+ response.httpStatusCode());
+}
+
+static void printErrorDescription(const WebURLError& error)
+{
+ string domain = error.domain.utf8();
+ int code = error.reason;
+
+ if (domain == net::kErrorDomain) {
+ domain = "NSURLErrorDomain";
+ switch (error.reason) {
+ case net::ERR_ABORTED:
+ code = -999;
+ break;
+ case net::ERR_UNSAFE_PORT:
+ // Our unsafe port checking happens at the network stack level, but we
+ // make this translation here to match the behavior of stock WebKit.
+ domain = "WebKitErrorDomain";
+ code = 103;
+ break;
+ case net::ERR_ADDRESS_INVALID:
+ case net::ERR_ADDRESS_UNREACHABLE:
+ code = -1004;
+ break;
+ }
+ } else
+ LOG_ERROR("Unknown error domain");
+
+ printf("<NSError domain %s, code %d, failing URL \"%s\">",
+ domain.c_str(), code, error.unreachableURL.spec().data());
+}
+
+static void printNodeDescription(const WebNode& node, int exception)
+{
+ if (exception) {
+ fputs("ERROR", stdout);
+ return;
+ }
+ if (node.isNull()) {
+ fputs("(null)", stdout);
+ return;
+ }
+ fputs(node.nodeName().utf8().data(), stdout);
+ const WebNode& parent = node.parentNode();
+ if (!parent.isNull()) {
+ fputs(" > ", stdout);
+ printNodeDescription(parent, 0);
+ }
+}
+
+static void printRangeDescription(const WebRange& range)
+{
+ if (range.isNull()) {
+ fputs("(null)", stdout);
+ return;
+ }
+ printf("range from %d of ", range.startOffset());
+ int exception = 0;
+ WebNode startNode = range.startContainer(exception);
+ printNodeDescription(startNode, exception);
+ printf(" to %d of ", range.endOffset());
+ WebNode endNode = range.endContainer(exception);
+ printNodeDescription(endNode, exception);
+}
+
+static string editingActionDescription(WebEditingAction action)
+{
+ switch (action) {
+ case WebKit::WebEditingActionTyped:
+ return "WebViewInsertActionTyped";
+ case WebKit::WebEditingActionPasted:
+ return "WebViewInsertActionPasted";
+ case WebKit::WebEditingActionDropped:
+ return "WebViewInsertActionDropped";
+ }
+ return "(UNKNOWN ACTION)";
+}
+
+static string textAffinityDescription(WebTextAffinity affinity)
+{
+ switch (affinity) {
+ case WebKit::WebTextAffinityUpstream:
+ return "NSSelectionAffinityUpstream";
+ case WebKit::WebTextAffinityDownstream:
+ return "NSSelectionAffinityDownstream";
+ }
+ return "(UNKNOWN AFFINITY)";
+}
+
+// WebViewClient -------------------------------------------------------------
+
+WebView* WebViewHost::createView(WebFrame*)
+{
+ if (!layoutTestController()->canOpenWindows())
+ return 0;
+ return m_shell->createWebView()->webView();
+}
+
+WebWidget* WebViewHost::createPopupMenu(bool)
+{
+ return 0;
+}
+
+WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&)
+{
+ return 0;
+}
+
+WebStorageNamespace* WebViewHost::createSessionStorageNamespace()
+{
+ return WebKit::WebStorageNamespace::createSessionStorageNamespace();
+}
+
+void WebViewHost::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine)
+{
+ // This matches win DumpRenderTree's UIDelegate.cpp.
+ string newMessage;
+ if (!message.text.isEmpty()) {
+ newMessage = message.text.utf8();
+ size_t fileProtocol = newMessage.find("file://");
+ if (fileProtocol != string::npos) {
+ newMessage = newMessage.substr(0, fileProtocol)
+ + urlSuitableForTestResult(newMessage.substr(fileProtocol));
+ }
+ }
+ printf("CONSOLE MESSAGE: line %d: %s\n", sourceLine, newMessage.data());
+}
+
+void WebViewHost::didStartLoading()
+{
+ m_shell->setIsLoading(true);
+}
+
+void WebViewHost::didStopLoading()
+{
+ m_shell->setIsLoading(false);
+}
+
+// The output from these methods in layout test mode should match that
+// expected by the layout tests. See EditingDelegate.m in DumpRenderTree.
+
+bool WebViewHost::shouldBeginEditing(const WebRange& range)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ fputs("EDITING DELEGATE: shouldBeginEditingInDOMRange:", stdout);
+ printRangeDescription(range);
+ fputs("\n", stdout);
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldEndEditing(const WebRange& range)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ fputs("EDITING DELEGATE: shouldEndEditingInDOMRange:", stdout);
+ printRangeDescription(range);
+ fputs("\n", stdout);
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldInsertNode(const WebNode& node, const WebRange& range, WebEditingAction action)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ fputs("EDITING DELEGATE: shouldInsertNode:", stdout);
+ printNodeDescription(node, 0);
+ fputs(" replacingDOMRange:", stdout);
+ printRangeDescription(range);
+ printf(" givenAction:%s\n", editingActionDescription(action).c_str());
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldInsertText(const WebString& text, const WebRange& range, WebEditingAction action)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:", text.utf8().data());
+ printRangeDescription(range);
+ printf(" givenAction:%s\n", editingActionDescription(action).c_str());
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldChangeSelectedRange(
+ const WebRange& fromRange, const WebRange& toRange, WebTextAffinity affinity, bool stillSelecting)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ fputs("EDITING DELEGATE: shouldChangeSelectedDOMRange:", stdout);
+ printRangeDescription(fromRange);
+ fputs(" toDOMRange:", stdout);
+ printRangeDescription(toRange);
+ printf(" affinity:%s stillSelecting:%s\n",
+ textAffinityDescription(affinity).c_str(),
+ (stillSelecting ? "TRUE" : "FALSE"));
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldDeleteRange(const WebRange& range)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ fputs("EDITING DELEGATE: shouldDeleteDOMRange:", stdout);
+ printRangeDescription(range);
+ fputs("\n", stdout);
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::shouldApplyStyle(const WebString& style, const WebRange& range)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:", style.utf8().data());
+ printRangeDescription(range);
+ fputs("\n", stdout);
+ }
+ return layoutTestController()->acceptsEditing();
+}
+
+bool WebViewHost::isSmartInsertDeleteEnabled()
+{
+ return m_smartInsertDeleteEnabled;
+}
+
+bool WebViewHost::isSelectTrailingWhitespaceEnabled()
+{
+ return m_selectTrailingWhitespaceEnabled;
+}
+
+void WebViewHost::didBeginEditing()
+{
+ if (!layoutTestController()->shouldDumpEditingCallbacks())
+ return;
+ fputs("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n", stdout);
+}
+
+void WebViewHost::didChangeSelection(bool isEmptySelection)
+{
+ if (layoutTestController()->shouldDumpEditingCallbacks())
+ fputs("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n", stdout);
+ // No need to update clipboard with the selected text in DRT.
+}
+
+void WebViewHost::didChangeContents()
+{
+ if (!layoutTestController()->shouldDumpEditingCallbacks())
+ return;
+ fputs("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n", stdout);
+}
+
+void WebViewHost::didEndEditing()
+{
+ if (!layoutTestController()->shouldDumpEditingCallbacks())
+ return;
+ fputs("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n", stdout);
+}
+
+bool WebViewHost::handleCurrentKeyboardEvent()
+{
+ if (m_editCommandName.empty())
+ return false;
+ WebFrame* frame = webView()->focusedFrame();
+ if (!frame)
+ return false;
+
+ return frame->executeCommand(WebString::fromUTF8(m_editCommandName), WebString::fromUTF8(m_editCommandValue));
+}
+
+void WebViewHost::spellCheck(const WebString& text, int& misspelledOffset, int& misspelledLength)
+{
+ // Check the spelling of the given text.
+#if OS(MAC_OS_X)
+ // FIXME: rebaseline layout-test results of Windows and Linux so we
+ // can enable this mock spellchecker on them.
+ m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength);
+#endif
+}
+
+WebString WebViewHost::autoCorrectWord(const WebString&)
+{
+ // Returns an empty string as Mac WebKit ('WebKitSupport/WebEditorClient.mm')
+ // does. (If this function returns a non-empty string, WebKit replaces the
+ // given misspelled string with the result one. This process executes some
+ // editor commands and causes layout-test failures.)
+ return WebString();
+}
+
+void WebViewHost::runModalAlertDialog(WebFrame*, const WebString& message)
+{
+ printf("ALERT: %s\n", message.utf8().data());
+}
+
+bool WebViewHost::runModalConfirmDialog(WebFrame*, const WebString& message)
+{
+ printf("CONFIRM: %s\n", message.utf8().data());
+ return true;
+}
+
+bool WebViewHost::runModalPromptDialog(WebFrame* frame, const WebString& message,
+ const WebString& defaultValue, WebString*)
+{
+ printf("PROMPT: %s, default text: %s\n", message.utf8().data(), defaultValue.utf8().data());
+ return true;
+}
+
+bool WebViewHost::runModalBeforeUnloadDialog(WebFrame*, const WebString&)
+{
+ return true; // Allow window closure.
+}
+
+void WebViewHost::showContextMenu(WebFrame*, const WebContextMenuData&)
+{
+}
+
+
+void WebViewHost::setStatusText(const WebString& text)
+{
+ if (!layoutTestController()->shouldDumpStatusCallbacks())
+ return;
+ // When running tests, write to stdout.
+ printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text.utf8().data());
+}
+
+void WebViewHost::startDragging(const WebPoint& mouseCoords, const WebDragData& data, WebDragOperationsMask mask)
+{
+ WebDragData mutableDragData = data;
+ if (layoutTestController()->shouldAddFileToPasteboard()) {
+ // Add a file called DRTFakeFile to the drag&drop clipboard.
+ addDRTFakeFileToDataObject(&mutableDragData);
+ }
+
+ // When running a test, we need to fake a drag drop operation otherwise
+ // Windows waits for real mouse events to know when the drag is over.
+ EventSender::doDragDrop(mouseCoords, mutableDragData, mask);
+}
+
+void WebViewHost::navigateBackForwardSoon(int offset)
+{
+ navigationController()->goToOffset(offset);
+}
+
+int WebViewHost::historyBackListCount()
+{
+ return navigationController()->lastCommittedEntryIndex();
+}
+
+int WebViewHost::historyForwardListCount()
+{
+ int currentIndex =navigationController()->lastCommittedEntryIndex();
+ return navigationController()->entryCount() - currentIndex - 1;
+}
+
+void WebViewHost::focusAccessibilityObject(const WebAccessibilityObject& object)
+{
+ m_shell->accessibilityController()->setFocusedElement(object);
+}
+
+// WebWidgetClient -----------------------------------------------------------
+
+void WebViewHost::didInvalidateRect(const WebRect& rect)
+{
+ if (m_isPainting)
+ LOG_ERROR("unexpected invalidation while painting");
+ updatePaintRect(rect);
+}
+
+void WebViewHost::didScrollRect(int, int, const WebRect& clipRect)
+{
+ // This is used for optimizing painting when the renderer is scrolled. We're
+ // currently not doing any optimizations so just invalidate the region.
+ didInvalidateRect(clipRect);
+}
+
+void WebViewHost::didFocus()
+{
+ m_shell->setFocus(webWidget(), true);
+}
+
+void WebViewHost::didBlur()
+{
+ m_shell->setFocus(webWidget(), false);
+}
+
+WebScreenInfo WebViewHost::screenInfo()
+{
+ // We don't need to set actual values.
+ WebScreenInfo info;
+ info.depth = 24;
+ info.depthPerComponent = 8;
+ info.isMonochrome = false;
+ info.rect = WebRect(0, 0, screenWidth, screenHeight);
+ // Use values different from info.rect for testing.
+ info.availableRect = WebRect(screenUnavailableBorder, screenUnavailableBorder,
+ screenWidth - screenUnavailableBorder * 2,
+ screenHeight - screenUnavailableBorder * 2);
+ return info;
+}
+
+void WebViewHost::show(WebNavigationPolicy)
+{
+ m_hasWindow = true;
+ WebSize size = webWidget()->size();
+ updatePaintRect(WebRect(0, 0, size.width, size.height));
+}
+
+void WebViewHost::closeWidgetSoon()
+{
+ m_hasWindow = false;
+ m_shell->closeWindow(this);
+}
+
+void WebViewHost::didChangeCursor(const WebCursorInfo& cursorInfo)
+{
+ if (!hasWindow())
+ return;
+ m_currentCursor = cursorInfo;
+}
+
+WebRect WebViewHost::windowRect()
+{
+ return m_windowRect;
+}
+
+void WebViewHost::setWindowRect(const WebRect& rect)
+{
+ m_windowRect = rect;
+ const int border2 = TestShell::virtualWindowBorder * 2;
+ if (m_windowRect.width <= border2)
+ m_windowRect.width = 1 + border2;
+ if (m_windowRect.height <= border2)
+ m_windowRect.height = 1 + border2;
+ int width = m_windowRect.width - border2;
+ int height = m_windowRect.height - border2;
+ discardBackingStore();
+ webWidget()->resize(WebSize(width, height));
+ updatePaintRect(WebRect(0, 0, width, height));
+}
+
+WebRect WebViewHost::rootWindowRect()
+{
+ return windowRect();
+}
+
+WebRect WebViewHost::windowResizerRect()
+{
+ // Not necessary.
+ return WebRect();
+}
+
+void WebViewHost::runModal()
+{
+ // FIXME: Should we implement this in DRT?
+}
+
+// WebFrameClient ------------------------------------------------------------
+
+WebPlugin* WebViewHost::createPlugin(WebFrame* frame, const WebPluginParams& params)
+{
+ return webkit_support::CreateWebPlugin(frame, params);
+}
+
+WebWorker* WebViewHost::createWorker(WebFrame*, WebWorkerClient*)
+{
+ return new TestWebWorker();
+}
+
+WebMediaPlayer* WebViewHost::createMediaPlayer(WebFrame* frame, WebMediaPlayerClient* client)
+{
+ return webkit_support::CreateMediaPlayer(frame, client);
+}
+
+bool WebViewHost::allowPlugins(WebFrame* frame, bool enabledPerSettings)
+{
+ return enabledPerSettings;
+}
+
+bool WebViewHost::allowImages(WebFrame* frame, bool enabledPerSettings)
+{
+ return enabledPerSettings;
+}
+
+void WebViewHost::loadURLExternally(WebFrame*, const WebURLRequest& request, WebNavigationPolicy policy)
+{
+ ASSERT(policy != WebKit::WebNavigationPolicyCurrentTab);
+ WebViewHost* another = m_shell->createNewWindow(request.url());
+ if (another)
+ another->show(policy);
+}
+
+WebNavigationPolicy WebViewHost::decidePolicyForNavigation(
+ WebFrame*, const WebURLRequest& request,
+ WebNavigationType type, const WebNode& originatingNode,
+ WebNavigationPolicy defaultPolicy, bool isRedirect)
+{
+ WebNavigationPolicy result;
+ if (!m_policyDelegateEnabled)
+ return defaultPolicy;
+
+ printf("Policy delegate: attempt to load %s with navigation type '%s'",
+ URLDescription(request.url()).c_str(), webNavigationTypeToString(type));
+ if (!originatingNode.isNull()) {
+ fputs(" originating from ", stdout);
+ printNodeDescription(originatingNode, 0);
+ }
+ fputs("\n", stdout);
+ if (m_policyDelegateIsPermissive)
+ result = WebKit::WebNavigationPolicyCurrentTab;
+ else
+ result = WebKit::WebNavigationPolicyIgnore;
+
+ if (m_policyDelegateShouldNotifyDone)
+ layoutTestController()->policyDelegateDone();
+ return result;
+}
+
+bool WebViewHost::canHandleRequest(WebFrame*, const WebURLRequest& request)
+{
+ GURL url = request.url();
+ // Just reject the scheme used in
+ // LayoutTests/http/tests/misc/redirect-to-external-url.html
+ return !url.SchemeIs("spaceballs");
+}
+
+WebURLError WebViewHost::cannotHandleRequestError(WebFrame*, const WebURLRequest& request)
+{
+ WebURLError error;
+ // A WebKit layout test expects the following values.
+ // unableToImplementPolicyWithError() below prints them.
+ error.domain = WebString::fromUTF8("WebKitErrorDomain");
+ error.reason = 101;
+ error.unreachableURL = request.url();
+ return error;
+}
+
+WebURLError WebViewHost::cancelledError(WebFrame*, const WebURLRequest& request)
+{
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_ABORTED;
+ error.unreachableURL = request.url();
+ return error;
+}
+
+void WebViewHost::unableToImplementPolicyWithError(WebFrame* frame, const WebURLError& error)
+{
+ printf("Policy delegate: unable to implement policy with error domain '%s', "
+ "error code %d, in frame '%s'\n",
+ error.domain.utf8().data(), error.reason, frame->name().utf8().data());
+}
+
+void WebViewHost::willPerformClientRedirect(WebFrame* frame, const WebURL& from, const WebURL& to,
+ double interval, double fire_time)
+{
+ if (!m_shell->shouldDumpFrameLoadCallbacks())
+ return;
+ printFrameDescription(frame);
+ printf(" - willPerformClientRedirectToURL: %s \n", to.spec().data());
+}
+
+void WebViewHost::didCancelClientRedirect(WebFrame* frame)
+{
+ if (!m_shell->shouldDumpFrameLoadCallbacks())
+ return;
+ printFrameDescription(frame);
+ fputs(" - didCancelClientRedirectForFrame\n", stdout);
+}
+
+void WebViewHost::didCreateDataSource(WebFrame*, WebDataSource* ds)
+{
+ ds->setExtraData(m_pendingExtraData.release());
+}
+
+void WebViewHost::didStartProvisionalLoad(WebFrame* frame)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didStartProvisionalLoadForFrame\n", stdout);
+ }
+
+ if (!m_topLoadingFrame)
+ m_topLoadingFrame = frame;
+
+ if (layoutTestController()->stopProvisionalFrameLoads()) {
+ printFrameDescription(frame);
+ fputs(" - stopping load in didStartProvisionalLoadForFrame callback\n", stdout);
+ frame->stopLoading();
+ }
+ updateAddressBar(frame->view());
+}
+
+void WebViewHost::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didReceiveServerRedirectForProvisionalLoadForFrame\n", stdout);
+ }
+ updateAddressBar(frame->view());
+}
+
+void WebViewHost::didFailProvisionalLoad(WebFrame* frame, const WebURLError& error)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didFailProvisionalLoadWithError\n", stdout);
+ }
+
+ locationChangeDone(frame);
+
+ // Don't display an error page if we're running layout tests, because
+ // DumpRenderTree doesn't.
+}
+
+void WebViewHost::didCommitProvisionalLoad(WebFrame* frame, bool isNewNavigation)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didCommitLoadForFrame\n", stdout);
+ }
+ updateForCommittedLoad(frame, isNewNavigation);
+}
+
+void WebViewHost::didClearWindowObject(WebFrame* frame)
+{
+ m_shell->bindJSObjectsToWindow(frame);
+}
+
+void WebViewHost::didReceiveTitle(WebFrame* frame, const WebString& title)
+{
+ WebCString title8 = title.utf8();
+
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ printf(" - didReceiveTitle: %s\n", title8.data());
+ }
+
+ if (layoutTestController()->shouldDumpTitleChanges())
+ printf("TITLE CHANGED: %s\n", title8.data());
+
+ setPageTitle(title);
+}
+
+void WebViewHost::didFinishDocumentLoad(WebFrame* frame)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didFinishDocumentLoadForFrame\n", stdout);
+ } else {
+ unsigned pendingUnloadEvents = frame->unloadListenerCount();
+ if (pendingUnloadEvents) {
+ printFrameDescription(frame);
+ printf(" - has %u onunload handler(s)\n", pendingUnloadEvents);
+ }
+ }
+}
+
+void WebViewHost::didHandleOnloadEvents(WebFrame* frame)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didHandleOnloadEventsForFrame\n", stdout);
+ }
+}
+
+void WebViewHost::didFailLoad(WebFrame* frame, const WebURLError& error)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didFailLoadWithError\n", stdout);
+ }
+ locationChangeDone(frame);
+}
+
+void WebViewHost::didFinishLoad(WebFrame* frame)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didFinishLoadForFrame\n", stdout);
+ }
+ updateAddressBar(frame->view());
+ locationChangeDone(frame);
+}
+
+void WebViewHost::didChangeLocationWithinPage(WebFrame* frame, bool isNewNavigation)
+{
+ frame->dataSource()->setExtraData(m_pendingExtraData.release());
+ if (m_shell->shouldDumpFrameLoadCallbacks()) {
+ printFrameDescription(frame);
+ fputs(" - didChangeLocationWithinPageForFrame\n", stdout);
+ }
+ updateForCommittedLoad(frame, isNewNavigation);
+}
+
+void WebViewHost::assignIdentifierToRequest(WebFrame*, unsigned identifier, const WebURLRequest& request)
+{
+ if (!m_shell->shouldDumpResourceLoadCallbacks())
+ return;
+ m_resourceIdentifierMap.set(identifier, descriptionSuitableForTestResult(request.url().spec()));
+}
+
+void WebViewHost::willSendRequest(WebFrame*, unsigned identifier, WebURLRequest& request, const WebURLResponse& redirectResponse)
+{
+ // Need to use GURL for host() and SchemeIs()
+ GURL url = request.url();
+ string requestURL = url.possibly_invalid_spec();
+
+ if (layoutTestController()->shouldDumpResourceLoadCallbacks()) {
+ GURL mainDocumentURL = request.firstPartyForCookies();
+ printResourceDescription(identifier);
+ printf(" - willSendRequest <NSURLRequest URL %s, main document URL %s,"
+ " http method %s> redirectResponse %s\n",
+ descriptionSuitableForTestResult(requestURL).c_str(),
+ URLDescription(mainDocumentURL).c_str(),
+ request.httpMethod().utf8().data());
+ printResponseDescription(redirectResponse);
+ fputs("\n", stdout);
+ }
+
+ if (!redirectResponse.isNull() && m_blocksRedirects) {
+ fputs("Returning null for this redirect\n", stdout);
+ // To block the request, we set its URL to an empty one.
+ request.setURL(WebURL());
+ return;
+ }
+
+ if (m_requestReturnNull) {
+ // To block the request, we set its URL to an empty one.
+ request.setURL(WebURL());
+ return;
+ }
+
+ string host = url.host();
+ // 255.255.255.255 is used in some tests that expect to get back an error.
+ if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https"))
+ && host != "127.0.0.1"
+ && host != "255.255.255.255"
+ && host != "localhost") {
+ printf("Blocked access to external URL %s\n", requestURL.c_str());
+
+ // To block the request, we set its URL to an empty one.
+ request.setURL(WebURL());
+ return;
+ }
+
+ // Set the new substituted URL.
+ request.setURL(webkit_support::RewriteLayoutTestsURL(request.url().spec()));
+}
+
+void WebViewHost::didReceiveResponse(WebFrame*, unsigned identifier, const WebURLResponse& response)
+{
+ if (!m_shell->shouldDumpResourceLoadCallbacks())
+ return;
+ printResourceDescription(identifier);
+ fputs(" - didReceiveResponse ", stdout);
+ printResponseDescription(response);
+ fputs("\n", stdout);
+}
+
+void WebViewHost::didFinishResourceLoad(WebFrame*, unsigned identifier)
+{
+ if (m_shell->shouldDumpResourceLoadCallbacks()) {
+ printResourceDescription(identifier);
+ fputs(" - didFinishLoading\n", stdout);
+ }
+ m_resourceIdentifierMap.remove(identifier);
+}
+
+void WebViewHost::didFailResourceLoad(WebFrame*, unsigned identifier, const WebURLError& error)
+{
+ if (m_shell->shouldDumpResourceLoadCallbacks()) {
+ printResourceDescription(identifier);
+ fputs(" - didFailLoadingWithError: ", stdout);
+ printErrorDescription(error);
+ fputs("\n", stdout);
+ }
+ m_resourceIdentifierMap.remove(identifier);
+}
+
+void WebViewHost::didDisplayInsecureContent(WebFrame*)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks())
+ fputs("didDisplayInsecureContent\n", stdout);
+}
+
+void WebViewHost::didRunInsecureContent(WebFrame*, const WebSecurityOrigin& origin)
+{
+ if (m_shell->shouldDumpFrameLoadCallbacks())
+ fputs("didRunInsecureContent\n", stdout);
+}
+
+bool WebViewHost::allowScript(WebFrame*, bool enabledPerSettings)
+{
+ return enabledPerSettings;
+}
+
+// Public functions -----------------------------------------------------------
+
+WebViewHost::WebViewHost(TestShell* shell)
+ : m_policyDelegateEnabled(false)
+ , m_policyDelegateIsPermissive(false)
+ , m_policyDelegateShouldNotifyDone(false)
+ , m_shell(shell)
+ , m_topLoadingFrame(0)
+ , m_hasWindow(false)
+ , m_pageId(-1)
+ , m_lastPageIdUpdated(-1)
+ , m_smartInsertDeleteEnabled(true)
+#if OS(WINDOWS)
+ , m_selectTrailingWhitespaceEnabled(true)
+#else
+ , m_selectTrailingWhitespaceEnabled(false)
+#endif
+ , m_blocksRedirects(false)
+ , m_requestReturnNull(false)
+ , m_isPainting(false)
+ , m_webWidget(0)
+{
+ m_navigationController.set(new TestNavigationController(this));
+}
+
+WebViewHost::~WebViewHost()
+{
+}
+
+WebView* WebViewHost::webView() const
+{
+ ASSERT(m_webWidget);
+ // DRT does not support popup widgets. So m_webWidget is always a WebView.
+ return static_cast<WebView*>(m_webWidget);
+}
+
+WebWidget* WebViewHost::webWidget() const
+{
+ ASSERT(m_webWidget);
+ return m_webWidget;
+}
+
+void WebViewHost::reset()
+{
+ // Do a little placement new dance...
+ TestShell* shell = m_shell;
+ WebWidget* widget = m_webWidget;
+ this->~WebViewHost();
+ new (this) WebViewHost(shell);
+ setWebWidget(widget);
+}
+
+void WebViewHost::setSelectTrailingWhitespaceEnabled(bool enabled)
+{
+ m_selectTrailingWhitespaceEnabled = enabled;
+ // In upstream WebKit, smart insert/delete is mutually exclusive with select
+ // trailing whitespace, however, we allow both because Chromium on Windows
+ // allows both.
+}
+
+void WebViewHost::setSmartInsertDeleteEnabled(bool enabled)
+{
+ m_smartInsertDeleteEnabled = enabled;
+ // In upstream WebKit, smart insert/delete is mutually exclusive with select
+ // trailing whitespace, however, we allow both because Chromium on Windows
+ // allows both.
+}
+
+void WebViewHost::setCustomPolicyDelegate(bool isCustom, bool isPermissive)
+{
+ m_policyDelegateEnabled = isCustom;
+ m_policyDelegateIsPermissive = isPermissive;
+}
+
+void WebViewHost::waitForPolicyDelegate()
+{
+ m_policyDelegateEnabled = true;
+ m_policyDelegateShouldNotifyDone = true;
+}
+
+void WebViewHost::setEditCommand(const string& name, const string& value)
+{
+ m_editCommandName = name;
+ m_editCommandValue = value;
+}
+
+void WebViewHost::clearEditCommand()
+{
+ m_editCommandName.clear();
+ m_editCommandValue.clear();
+}
+
+void WebViewHost::loadURLForFrame(const WebURL& url, const WebString& frameName)
+{
+ if (!url.isValid())
+ return;
+ TestShell::resizeWindowForTest(this, url);
+ navigationController()->loadEntry(new TestNavigationEntry(-1, url, WebString(), frameName));
+}
+
+bool WebViewHost::navigate(const TestNavigationEntry& entry, bool reload)
+{
+ // Get the right target frame for the entry.
+ WebFrame* frame = webView()->mainFrame();
+ if (!entry.targetFrame().isEmpty())
+ frame = webView()->findFrameByName(entry.targetFrame());
+
+ // TODO(mpcomplete): should we clear the target frame, or should
+ // back/forward navigations maintain the target frame?
+
+ // A navigation resulting from loading a javascript URL should not be
+ // treated as a browser initiated event. Instead, we want it to look as if
+ // the page initiated any load resulting from JS execution.
+ if (!GURL(entry.URL()).SchemeIs("javascript"))
+ setPendingExtraData(new TestShellExtraData(entry.pageID()));
+
+ // If we are reloading, then WebKit will use the state of the current page.
+ // Otherwise, we give it the state to navigate to.
+ if (reload) {
+ frame->reload(false);
+ } else if (!entry.contentState().isNull()) {
+ ASSERT(entry.pageID() != -1);
+ frame->loadHistoryItem(entry.contentState());
+ } else {
+ ASSERT(entry.pageID() == -1);
+ frame->loadRequest(WebURLRequest(entry.URL()));
+ }
+
+ // In case LoadRequest failed before DidCreateDataSource was called.
+ setPendingExtraData(0);
+
+ // Restore focus to the main frame prior to loading new request.
+ // This makes sure that we don't have a focused iframe. Otherwise, that
+ // iframe would keep focus when the SetFocus called immediately after
+ // LoadRequest, thus making some tests fail (see http://b/issue?id=845337
+ // for more details).
+ webView()->setFocusedFrame(frame);
+ m_shell->setFocus(webView(), true);
+
+ return true;
+}
+
+// Private functions ----------------------------------------------------------
+
+LayoutTestController* WebViewHost::layoutTestController() const
+{
+ return m_shell->layoutTestController();
+}
+
+void WebViewHost::updateAddressBar(WebView* webView)
+{
+ WebFrame* mainFrame = webView->mainFrame();
+ WebDataSource* dataSource = mainFrame->dataSource();
+ if (!dataSource)
+ dataSource = mainFrame->provisionalDataSource();
+ if (!dataSource)
+ return;
+
+ setAddressBarURL(dataSource->request().firstPartyForCookies());
+}
+
+void WebViewHost::locationChangeDone(WebFrame* frame)
+{
+ if (frame != m_topLoadingFrame)
+ return;
+ m_topLoadingFrame = 0;
+ layoutTestController()->locationChangeDone();
+}
+
+void WebViewHost::updateForCommittedLoad(WebFrame* frame, bool isNewNavigation)
+{
+ // Code duplicated from RenderView::DidCommitLoadForFrame.
+ TestShellExtraData* extraData = static_cast<TestShellExtraData*>(frame->dataSource()->extraData());
+
+ if (isNewNavigation) {
+ // New navigation.
+ updateSessionHistory(frame);
+ m_pageId = nextPageID++;
+ } else if (extraData && extraData->pendingPageID != -1 && !extraData->requestCommitted) {
+ // This is a successful session history navigation!
+ updateSessionHistory(frame);
+ m_pageId = extraData->pendingPageID;
+ }
+
+ // Don't update session history multiple times.
+ if (extraData)
+ extraData->requestCommitted = true;
+
+ updateURL(frame);
+}
+
+void WebViewHost::updateURL(WebFrame* frame)
+{
+ WebDataSource* ds = frame->dataSource();
+ ASSERT(ds);
+ const WebURLRequest& request = ds->request();
+ OwnPtr<TestNavigationEntry> entry(new TestNavigationEntry);
+
+ // The referrer will be empty on https->http transitions. It
+ // would be nice if we could get the real referrer from somewhere.
+ entry->setPageID(m_pageId);
+ if (ds->hasUnreachableURL())
+ entry->setURL(ds->unreachableURL());
+ else
+ entry->setURL(request.url());
+
+ const WebHistoryItem& historyItem = frame->currentHistoryItem();
+ if (!historyItem.isNull())
+ entry->setContentState(historyItem);
+
+ navigationController()->didNavigateToEntry(entry.release());
+ m_lastPageIdUpdated = max(m_lastPageIdUpdated, m_pageId);
+}
+
+void WebViewHost::updateSessionHistory(WebFrame* frame)
+{
+ // If we have a valid page ID at this point, then it corresponds to the page
+ // we are navigating away from. Otherwise, this is the first navigation, so
+ // there is no past session history to record.
+ if (m_pageId == -1)
+ return;
+
+ TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(navigationController()->entryWithPageID(m_pageId));
+ if (!entry)
+ return;
+
+ const WebHistoryItem& historyItem = webView()->mainFrame()->previousHistoryItem();
+ if (historyItem.isNull())
+ return;
+
+ entry->setContentState(historyItem);
+}
+
+void WebViewHost::printFrameDescription(WebFrame* webframe)
+{
+ string name8 = webframe->name().utf8();
+ if (webframe == webView()->mainFrame()) {
+ if (!name8.length()) {
+ fputs("main frame", stdout);
+ return;
+ }
+ printf("main frame \"%s\"", name8.c_str());
+ return;
+ }
+ if (!name8.length()) {
+ fputs("frame (anonymous)", stdout);
+ return;
+ }
+ printf("frame \"%s\"", name8.c_str());
+}
+
+void WebViewHost::printResourceDescription(unsigned identifier)
+{
+ ResourceMap::iterator it = m_resourceIdentifierMap.find(identifier);
+ printf("%s", it != m_resourceIdentifierMap.end() ? it->second.c_str() : "<unknown>");
+}
+
+void WebViewHost::setPendingExtraData(TestShellExtraData* extraData)
+{
+ m_pendingExtraData.set(extraData);
+}
+
+void WebViewHost::setPageTitle(const WebString&)
+{
+ // Nothing to do in layout test.
+}
+
+void WebViewHost::setAddressBarURL(const WebURL&)
+{
+ // Nothing to do in layout test.
+}
+
+// Painting functions ---------------------------------------------------------
+
+void WebViewHost::updatePaintRect(const WebRect& rect)
+{
+ // m_paintRect = m_paintRect U rect
+ if (rect.isEmpty())
+ return;
+ if (m_paintRect.isEmpty()) {
+ m_paintRect = rect;
+ return;
+ }
+ int left = min(m_paintRect.x, rect.x);
+ int top = min(m_paintRect.y, rect.y);
+ int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width);
+ int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height);
+ m_paintRect = WebRect(left, top, right - left, bottom - top);
+}
+
+void WebViewHost::paintRect(const WebRect& rect)
+{
+ ASSERT(!m_isPainting);
+ ASSERT(canvas());
+ m_isPainting = true;
+#if PLATFORM(CG)
+ webWidget()->paint(m_canvas->getTopPlatformDevice().GetBitmapContext(), rect);
+#else
+ webWidget()->paint(m_canvas.get(), rect);
+#endif
+ m_isPainting = false;
+}
+
+void WebViewHost::paintInvalidatedRegion()
+{
+ webWidget()->layout();
+ WebSize widgetSize = webWidget()->size();
+ WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
+
+ // Paint the canvas if necessary. Allow painting to generate extra rects the
+ // first time we call it. This is necessary because some WebCore rendering
+ // objects update their layout only when painted.
+ // Store the total area painted in total_paint. Then tell the gdk window
+ // to update that area after we're done painting it.
+ for (int i = 0; i < 2; ++i) {
+ // m_paintRect = intersect(m_paintRect , clientRect)
+ int left = max(m_paintRect.x, clientRect.x);
+ int top = max(m_paintRect.y, clientRect.y);
+ int right = min(m_paintRect.x + m_paintRect.width, clientRect.x + clientRect.width);
+ int bottom = min(m_paintRect.y + m_paintRect.height, clientRect.y + clientRect.height);
+ if (left >= right || top >= bottom)
+ m_paintRect = WebRect();
+ else
+ m_paintRect = WebRect(left, top, right - left, bottom - top);
+
+ if (m_paintRect.isEmpty())
+ continue;
+ WebRect rect(m_paintRect);
+ m_paintRect = WebRect();
+ paintRect(rect);
+ if (i == 1)
+ LOG_ERROR("painting caused additional invalidations");
+ }
+ ASSERT(m_paintRect.isEmpty());
+}
+
+PlatformCanvas* WebViewHost::canvas()
+{
+ if (m_canvas)
+ return m_canvas.get();
+ WebSize widgetSize = webWidget()->size();
+ resetScrollRect();
+ m_paintRect = WebRect(0, 0, widgetSize.width, widgetSize.height);
+ m_canvas.set(new PlatformCanvas(widgetSize.width, widgetSize.height, true));
+ return m_canvas.get();
+}
+
+void WebViewHost::resetScrollRect()
+{
+}
+
+void WebViewHost::discardBackingStore()
+{
+ m_canvas.clear();
+}
+
+// Paints the entire canvas a semi-transparent black (grayish). This is used
+// by the layout tests in fast/repaint. The alpha value matches upstream.
+void WebViewHost::displayRepaintMask()
+{
+ canvas()->drawARGB(167, 0, 0, 0);
+}
diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.h b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h
new file mode 100644
index 0000000..fd1e0f5
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebViewHost_h
+#define WebViewHost_h
+
+#include "MockSpellCheck.h"
+#include "TestNavigationController.h"
+#include "public/WebCursorInfo.h"
+#include "public/WebFrameClient.h"
+#include "public/WebViewClient.h"
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
+class LayoutTestController;
+class TestShell;
+namespace WebKit {
+class WebFrame;
+class WebURL;
+struct WebURLError;
+struct WebRect;
+}
+namespace skia {
+class PlatformCanvas;
+}
+
+class WebViewHost : public WebKit::WebViewClient, public WebKit::WebFrameClient, public NavigationHost {
+ public:
+ WebViewHost(TestShell* shell);
+ ~WebViewHost();
+ void setWebWidget(WebKit::WebWidget* widget) { m_webWidget = widget; }
+ WebKit::WebView* webView() const;
+ WebKit::WebWidget* webWidget() const;
+ void reset();
+ void setSelectTrailingWhitespaceEnabled(bool);
+ void setSmartInsertDeleteEnabled(bool);
+ void waitForPolicyDelegate();
+ void setCustomPolicyDelegate(bool, bool);
+ WebKit::WebFrame* topLoadingFrame() { return m_topLoadingFrame; }
+ void setBlockRedirects(bool block) { m_blocksRedirects = block; }
+ void setRequestReturnNull(bool returnNull) { m_requestReturnNull = returnNull; }
+ void setEditCommand(const std::string& name, const std::string& value);
+ void clearEditCommand();
+ void setPendingExtraData(TestShellExtraData*);
+
+ void paintRect(const WebKit::WebRect&);
+ void updatePaintRect(const WebKit::WebRect&);
+ void paintInvalidatedRegion();
+ skia::PlatformCanvas* canvas();
+ void displayRepaintMask();
+
+ void loadURLForFrame(const WebKit::WebURL&, const WebKit::WebString& frameName);
+ TestNavigationController* navigationController() { return m_navigationController.get(); }
+
+ // NavigationHost
+ virtual bool navigate(const TestNavigationEntry&, bool reload);
+
+ // WebKit::WebViewClient
+ virtual WebKit::WebView* createView(WebKit::WebFrame*);
+ virtual WebKit::WebWidget* createPopupMenu(bool activatable);
+ virtual WebKit::WebWidget* createPopupMenu(const WebKit::WebPopupMenuInfo&);
+ virtual WebKit::WebStorageNamespace* createSessionStorageNamespace();
+ virtual void didAddMessageToConsole(const WebKit::WebConsoleMessage&, const WebKit::WebString& sourceName, unsigned sourceLine);
+ virtual void didStartLoading();
+ virtual void didStopLoading();
+ virtual bool shouldBeginEditing(const WebKit::WebRange&);
+ virtual bool shouldEndEditing(const WebKit::WebRange&);
+ virtual bool shouldInsertNode(const WebKit::WebNode&, const WebKit::WebRange&, WebKit::WebEditingAction);
+ virtual bool shouldInsertText(const WebKit::WebString&, const WebKit::WebRange&, WebKit::WebEditingAction);
+ virtual bool shouldChangeSelectedRange(const WebKit::WebRange& from, const WebKit::WebRange& to, WebKit::WebTextAffinity, bool stillSelecting);
+ virtual bool shouldDeleteRange(const WebKit::WebRange&);
+ virtual bool shouldApplyStyle(const WebKit::WebString& style, const WebKit::WebRange&);
+ virtual bool isSmartInsertDeleteEnabled();
+ virtual bool isSelectTrailingWhitespaceEnabled();
+ virtual void didBeginEditing();
+ virtual void didChangeSelection(bool isSelectionEmpty);
+ virtual void didChangeContents();
+ virtual void didEndEditing();
+ virtual bool handleCurrentKeyboardEvent();
+ virtual void spellCheck(const WebKit::WebString&, int& offset, int& length);
+ virtual WebKit::WebString autoCorrectWord(const WebKit::WebString&);
+ virtual void runModalAlertDialog(WebKit::WebFrame*, const WebKit::WebString&);
+ virtual bool runModalConfirmDialog(WebKit::WebFrame*, const WebKit::WebString&);
+ virtual bool runModalPromptDialog(WebKit::WebFrame*, const WebKit::WebString& message, const WebKit::WebString& defaultValue, WebKit::WebString* actualValue);
+ virtual bool runModalBeforeUnloadDialog(WebKit::WebFrame*, const WebKit::WebString&);
+ virtual void showContextMenu(WebKit::WebFrame*, const WebKit::WebContextMenuData&);
+ virtual void setStatusText(const WebKit::WebString&);
+ virtual void startDragging(const WebKit::WebPoint&, const WebKit::WebDragData&, WebKit::WebDragOperationsMask);
+ virtual void navigateBackForwardSoon(int offset);
+ virtual int historyBackListCount();
+ virtual int historyForwardListCount();
+ virtual void focusAccessibilityObject(const WebKit::WebAccessibilityObject&);
+
+ // WebKit::WebWidgetClient
+ virtual void didInvalidateRect(const WebKit::WebRect&);
+ virtual void didScrollRect(int dx, int dy, const WebKit::WebRect&);
+ virtual void didFocus();
+ virtual void didBlur();
+ virtual void didChangeCursor(const WebKit::WebCursorInfo&);
+ virtual void closeWidgetSoon();
+ virtual void show(WebKit::WebNavigationPolicy);
+ virtual void runModal();
+ virtual WebKit::WebRect windowRect();
+ virtual void setWindowRect(const WebKit::WebRect&);
+ virtual WebKit::WebRect rootWindowRect();
+ virtual WebKit::WebRect windowResizerRect();
+ virtual WebKit::WebScreenInfo screenInfo();
+
+ // WebKit::WebFrameClient
+ virtual WebKit::WebPlugin* createPlugin(WebKit::WebFrame*, const WebKit::WebPluginParams&);
+ virtual WebKit::WebWorker* createWorker(WebKit::WebFrame*, WebKit::WebWorkerClient*);
+ virtual WebKit::WebMediaPlayer* createMediaPlayer(WebKit::WebFrame*, WebKit::WebMediaPlayerClient*);
+ virtual bool allowPlugins(WebKit::WebFrame*, bool enabledPerSettings);
+ virtual bool allowImages(WebKit::WebFrame*, bool enabledPerSettings);
+ virtual void loadURLExternally(WebKit::WebFrame*, const WebKit::WebURLRequest&, WebKit::WebNavigationPolicy);
+ virtual WebKit::WebNavigationPolicy decidePolicyForNavigation(
+ WebKit::WebFrame*, const WebKit::WebURLRequest&,
+ WebKit::WebNavigationType, const WebKit::WebNode&,
+ WebKit::WebNavigationPolicy, bool isRedirect);
+ virtual bool canHandleRequest(WebKit::WebFrame*, const WebKit::WebURLRequest&);
+ virtual WebKit::WebURLError cannotHandleRequestError(WebKit::WebFrame*, const WebKit::WebURLRequest&);
+ virtual WebKit::WebURLError cancelledError(WebKit::WebFrame*, const WebKit::WebURLRequest&);
+ virtual void unableToImplementPolicyWithError(WebKit::WebFrame*, const WebKit::WebURLError&);
+ virtual void willPerformClientRedirect(
+ WebKit::WebFrame*, const WebKit::WebURL& from, const WebKit::WebURL& to,
+ double interval, double fireTime);
+ virtual void didCancelClientRedirect(WebKit::WebFrame*);
+ virtual void didCreateDataSource(WebKit::WebFrame*, WebKit::WebDataSource*);
+ virtual void didStartProvisionalLoad(WebKit::WebFrame*);
+ virtual void didReceiveServerRedirectForProvisionalLoad(WebKit::WebFrame*);
+ virtual void didFailProvisionalLoad(WebKit::WebFrame*, const WebKit::WebURLError&);
+ virtual void didCommitProvisionalLoad(WebKit::WebFrame*, bool isNewNavigation);
+ virtual void didClearWindowObject(WebKit::WebFrame*);
+ virtual void didReceiveTitle(WebKit::WebFrame*, const WebKit::WebString&);
+ virtual void didFinishDocumentLoad(WebKit::WebFrame*);
+ virtual void didHandleOnloadEvents(WebKit::WebFrame*);
+ virtual void didFailLoad(WebKit::WebFrame*, const WebKit::WebURLError&);
+ virtual void didFinishLoad(WebKit::WebFrame*);
+ virtual void didChangeLocationWithinPage(WebKit::WebFrame*, bool isNewNavigation);
+ virtual void assignIdentifierToRequest(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLRequest&);
+ virtual void willSendRequest(WebKit::WebFrame*, unsigned identifier, WebKit::WebURLRequest&, const WebKit::WebURLResponse&);
+ virtual void didReceiveResponse(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLResponse&);
+ virtual void didFinishResourceLoad(WebKit::WebFrame*, unsigned identifier);
+ virtual void didFailResourceLoad(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLError&);
+ virtual void didDisplayInsecureContent(WebKit::WebFrame*);
+ virtual void didRunInsecureContent(WebKit::WebFrame*, const WebKit::WebSecurityOrigin&);
+ virtual bool allowScript(WebKit::WebFrame*, bool enabledPerSettings);
+
+private:
+ LayoutTestController* layoutTestController() const;
+
+ // Called the title of the page changes.
+ // Can be used to update the title of the window.
+ void setPageTitle(const WebKit::WebString&);
+
+ // Called when the URL of the page changes.
+ // Extracts the URL and forwards on to SetAddressBarURL().
+ void updateAddressBar(WebKit::WebView*);
+
+ // Called when the URL of the page changes.
+ // Should be used to update the text of the URL bar.
+ void setAddressBarURL(const WebKit::WebURL&);
+
+ // In the Mac code, this is called to trigger the end of a test after the
+ // page has finished loading. From here, we can generate the dump for the
+ // test.
+ void locationChangeDone(WebKit::WebFrame*);
+
+ void updateForCommittedLoad(WebKit::WebFrame*, bool isNewNavigation);
+ void updateURL(WebKit::WebFrame*);
+ void updateSessionHistory(WebKit::WebFrame*);
+
+ // Dumping a frame to the console.
+ void printFrameDescription(WebKit::WebFrame*);
+
+ bool hasWindow() const { return m_hasWindow; }
+ void resetScrollRect();
+ void discardBackingStore();
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ bool m_policyDelegateEnabled;
+
+ // Toggles the behavior of the policy delegate. If true, then navigations
+ // will be allowed. Otherwise, they will be ignored (dropped).
+ bool m_policyDelegateIsPermissive;
+
+ // If true, the policy delegate will signal layout test completion.
+ bool m_policyDelegateShouldNotifyDone;
+
+ // Non-owning pointer. The WebViewHost instance is owned by this TestShell instance.
+ TestShell* m_shell;
+
+ // This delegate works for the following widget.
+ WebKit::WebWidget* m_webWidget;
+
+ // This is non-0 IFF a load is in progress.
+ WebKit::WebFrame* m_topLoadingFrame;
+
+ // For tracking session history. See RenderView.
+ int m_pageId;
+ int m_lastPageIdUpdated;
+
+ OwnPtr<TestShellExtraData> m_pendingExtraData;
+
+ // Maps resource identifiers to a descriptive string.
+ typedef HashMap<unsigned, std::string> ResourceMap;
+ ResourceMap m_resourceIdentifierMap;
+ void printResourceDescription(unsigned identifier);
+
+ WebKit::WebCursorInfo m_currentCursor;
+
+ bool m_hasWindow;
+ WebKit::WebRect m_windowRect;
+
+ // true if we want to enable smart insert/delete.
+ bool m_smartInsertDeleteEnabled;
+
+ // true if we want to enable selection of trailing whitespaces
+ bool m_selectTrailingWhitespaceEnabled;
+
+ // true if we should block any redirects
+ bool m_blocksRedirects;
+
+ // true if we should block (set an empty request for) any requests
+ bool m_requestReturnNull;
+
+ // Edit command associated to the current keyboard event.
+ std::string m_editCommandName;
+ std::string m_editCommandValue;
+
+ // The mock spellchecker used in spellCheck().
+ MockSpellCheck m_spellcheck;
+
+ // Painting.
+ OwnPtr<skia::PlatformCanvas> m_canvas;
+ WebKit::WebRect m_paintRect;
+ bool m_isPainting;
+
+ OwnPtr<TestNavigationController*> m_navigationController;
+};
+
+#endif // WebViewHost_h
diff --git a/WebKitTools/DumpRenderTree/chromium/config.h b/WebKitTools/DumpRenderTree/chromium/config.h
new file mode 100644
index 0000000..6029532
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/chromium/config.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef config_h
+#define config_h
+
+// To avoid confict of LOG in wtf/Assertions.h and LOG in base/logging.h,
+// skip base/loggin.h by defining BASE_LOGGING_H_ and define some macros
+// provided by base/logging.h.
+// FIXME: Remove this hack!
+#include <iostream>
+#define BASE_LOGGING_H_
+#define CHECK(condition) while (false && (condition)) std::cerr
+#define DCHECK(condition) while (false && (condition)) std::cerr
+#define DCHECK_EQ(a, b) while (false && (a) == (b)) std::cerr
+#define DCHECK_NE(a, b) while (false && (a) != (b)) std::cerr
+
+#include <wtf/Platform.h>
+
+// JS_EXPORTDATA is needed to inlucde wtf/WTFString.h.
+#if OS(WINDOWS) && !COMPILER(GCC)
+#define JS_EXPORTDATA __declspec(dllimport)
+#else
+#define JS_EXPORTDATA
+#endif
+
+#endif // config_h
diff --git a/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png b/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png
new file mode 100644
index 0000000..8b9319c
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png
Binary files differ
diff --git a/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf b/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf
new file mode 100644
index 0000000..cd919e8
--- /dev/null
+++ b/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf
Binary files differ
diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp
index 12653fc..ab9f021 100644
--- a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp
@@ -46,6 +46,12 @@ AccessibilityController::~AccessibilityController()
{
}
+AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y)
+{
+ // FIXME: implement
+ return 0;
+}
+
AccessibilityUIElement AccessibilityController::focusedElement()
{
AtkObject* accessible = webkit_web_frame_get_focused_accessible_element(mainFrame);
diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp
index abfe115..fabada3 100644
--- a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp
@@ -75,6 +75,26 @@ void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>
}
}
+int AccessibilityUIElement::rowCount()
+{
+ if (!m_element)
+ return 0;
+
+ ASSERT(ATK_IS_TABLE(m_element));
+
+ return atk_table_get_n_rows(ATK_TABLE(m_element));
+}
+
+int AccessibilityUIElement::columnCount()
+{
+ if (!m_element)
+ return 0;
+
+ ASSERT(ATK_IS_TABLE(m_element));
+
+ return atk_table_get_n_columns(ATK_TABLE(m_element));
+}
+
int AccessibilityUIElement::childrenCount()
{
if (!m_element)
@@ -204,6 +224,11 @@ JSStringRef AccessibilityUIElement::language()
return JSStringCreateWithCharacters(0, 0);
}
+JSStringRef AccessibilityUIElement::helpText() const
+{
+ return 0;
+}
+
double AccessibilityUIElement::x()
{
int x, y;
@@ -492,6 +517,11 @@ void AccessibilityUIElement::decrement()
// FIXME: implement
}
+void AccessibilityUIElement::press()
+{
+ // FIXME: implement
+}
+
void AccessibilityUIElement::showMenu()
{
// FIXME: implement
@@ -558,6 +588,11 @@ bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallbac
return false;
}
+void AccessibilityUIElement::removeNotificationListener()
+{
+ // FIXME: implement
+}
+
bool AccessibilityUIElement::isSelectable() const
{
// FIXME: implement
diff --git a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp
index a2fc79b..e37613d 100644
--- a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp
@@ -117,6 +117,11 @@ static bool shouldOpenWebInspector(const char* pathOrURL)
return strstr(pathOrURL, "inspector/");
}
+static bool shouldEnableDeveloperExtras(const char* pathOrURL)
+{
+ return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "inspector-enabled/");
+}
+
void dumpFrameScrollPosition(WebKitWebFrame* frame)
{
@@ -319,6 +324,7 @@ static void resetDefaultsToConsistentValues()
"enable-html5-database", TRUE,
"enable-html5-local-storage", TRUE,
"enable-xss-auditor", FALSE,
+ "enable-spatial-navigation", FALSE,
"javascript-can-open-windows-automatically", TRUE,
"enable-offline-web-application-cache", TRUE,
"enable-universal-access-from-file-uris", TRUE,
@@ -335,6 +341,7 @@ static void resetDefaultsToConsistentValues()
"enable-page-cache", FALSE,
"auto-resize-window", TRUE,
"enable-java-applet", FALSE,
+ "enable-plugins", TRUE,
NULL);
webkit_web_frame_clear_main_frame_name(mainFrame);
@@ -346,6 +353,16 @@ static void resetDefaultsToConsistentValues()
webkit_reset_origin_access_white_lists();
+#ifdef HAVE_LIBSOUP_2_29_90
+ SoupSession* session = webkit_get_default_session();
+ SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
+
+ // We only create the jar when the soup backend needs to do
+ // HTTP. Should we initialize it earlier, perhaps?
+ if (jar)
+ g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL);
+#endif
+
setlocale(LC_ALL, "");
}
@@ -468,8 +485,11 @@ static void runTest(const string& testPathOrURL)
if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
gLayoutTestController->setDumpFrameLoadCallbacks(true);
- if (shouldOpenWebInspector(pathOrURL.c_str()))
- gLayoutTestController->showWebInspector();
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
+ gLayoutTestController->setDeveloperExtrasEnabled(true);
+ if (shouldOpenWebInspector(pathOrURL.c_str()))
+ gLayoutTestController->showWebInspector();
+ }
WorkQueue::shared()->clear();
WorkQueue::shared()->setFrozen(false);
@@ -502,7 +522,8 @@ static void runTest(const string& testPathOrURL)
gtk_main();
- if (shouldOpenWebInspector(pathOrURL.c_str()))
+ // If developer extras enabled Web Inspector may have been open by the test.
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str()))
gLayoutTestController->closeWebInspector();
// Also check if we still have opened webViews and free them.
@@ -550,7 +571,7 @@ static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFra
// This is a bit strange. Shouldn't web_frame_get_name return NULL?
if (frameName && (frameName[0] != '\0')) {
char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
- g_free (frameName);
+ g_free(frameName);
frameName = tmp;
} else {
g_free(frameName);
@@ -559,20 +580,31 @@ static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFra
} else if (!frameName || (frameName[0] == '\0')) {
g_free(frameName);
frameName = g_strdup("frame (anonymous)");
+ } else {
+ char* tmp = g_strdup_printf("frame \"%s\"", frameName);
+ g_free(frameName);
+ frameName = tmp;
}
return frameName;
}
+static void webViewLoadCommitted(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didCommitLoadForFrame\n", frameName);
+ g_free(frameName);
+ }
+}
+
+
static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
{
- if (!done && !gLayoutTestController->dumpFrameLoadCallbacks()) {
- guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
- if (pendingFrameUnloadEvents) {
- char* frameName = getFrameNameSuitableForTestResult(view, frame);
- printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
- g_free(frameName);
- }
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didFinishLoadForFrame\n", frameName);
+ g_free(frameName);
}
if (frame != topLoadingFrame)
@@ -589,6 +621,31 @@ static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void
dump();
}
+static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didFinishDocumentLoadForFrame\n", frameName);
+ g_free(frameName);
+ } else if (!done) {
+ guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
+ if (pendingFrameUnloadEvents) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
+ g_free(frameName);
+ }
+ }
+}
+
+static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didHandleOnloadEventsForFrame\n", frameName);
+ g_free(frameName);
+ }
+}
+
static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
{
JSValueRef exception = 0;
@@ -611,7 +668,26 @@ static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* fram
static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
{
- fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message);
+ gchar* testMessage = 0;
+ const gchar* uriScheme;
+
+ // Tests expect only the filename part of local URIs
+ uriScheme = g_strstr_len(message, -1, "file://");
+ if (uriScheme) {
+ GString* tempString = g_string_sized_new(strlen(message));
+ gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
+
+ if (filename) {
+ filename += strlen(G_DIR_SEPARATOR_S);
+ tempString = g_string_append_len(tempString, message, (uriScheme - message));
+ tempString = g_string_append_len(tempString, filename, strlen(filename));
+ testMessage = g_string_free(tempString, FALSE);
+ }
+ }
+
+ fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, testMessage ? testMessage : message);
+ g_free(testMessage);
+
return TRUE;
}
@@ -725,6 +801,19 @@ static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, We
webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
}
+static bool
+geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
+{
+ if (!gLayoutTestController->isGeolocationPermissionSet())
+ return FALSE;
+ if (gLayoutTestController->geolocationPermission())
+ webkit_geolocation_policy_allow(decision);
+ else
+ webkit_geolocation_policy_deny(decision);
+
+ return TRUE;
+}
+
static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
@@ -764,6 +853,7 @@ static WebKitWebView* createWebView()
g_object_connect(G_OBJECT(view),
"signal::load-started", webViewLoadStarted, 0,
"signal::load-finished", webViewLoadFinished, 0,
+ "signal::load-committed", webViewLoadCommitted, 0,
"signal::window-object-cleared", webViewWindowObjectCleared, 0,
"signal::console-message", webViewConsoleMessage, 0,
"signal::script-alert", webViewScriptAlert, 0,
@@ -775,6 +865,9 @@ static WebKitWebView* createWebView()
"signal::create-web-view", webViewCreate, 0,
"signal::close-web-view", webViewClose, 0,
"signal::database-quota-exceeded", databaseQuotaExceeded, 0,
+ "signal::document-load-finished", webViewDocumentLoadFinished, 0,
+ "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
+ "signal::onload-event", webViewOnloadEvent, 0,
NULL);
WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
@@ -806,11 +899,22 @@ static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
return newWebView;
}
+static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
+{
+ if (level < G_LOG_LEVEL_DEBUG)
+ fprintf(stderr, "%s\n", message);
+}
+
int main(int argc, char* argv[])
{
g_thread_init(NULL);
gtk_init(&argc, &argv);
+ // Some plugins might try to use the GLib logger for printing debug
+ // messages. This will cause tests to fail because of unexpected output.
+ // We squelch all debug messages sent to the logger.
+ g_log_set_default_handler(logHandler, 0);
+
#if PLATFORM(X11)
FcInit();
initializeFonts();
diff --git a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
index 458e0ba..9c27d8c 100644
--- a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
@@ -557,8 +557,18 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS
event.key.state = state;
event.key.window = GTK_WIDGET(view)->window;
+ // When synthesizing an event, an invalid hardware_keycode value
+ // can cause it to be badly processed by Gtk+.
+ GdkKeymapKey* keys;
+ gint n_keys;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys, &n_keys)) {
+ event.key.hardware_keycode = keys[0].keycode;
+ g_free(keys);
+ }
+
gboolean return_val;
event.key.type = GDK_KEY_PRESS;
+
g_signal_emit_by_name(view, "key-press-event", &event.key, &return_val);
event.key.type = GDK_KEY_RELEASE;
@@ -567,47 +577,49 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS
return JSValueMakeUndefined(context);
}
-static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static void zoomIn(gboolean fullContentsZoom)
{
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
if (!view)
- return JSValueMakeUndefined(context);
+ return;
+ webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
gfloat currentZoom = webkit_web_view_get_zoom_level(view);
webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio);
-
- return JSValueMakeUndefined(context);
}
-static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static void zoomOut(gboolean fullContentsZoom)
{
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
if (!view)
- return JSValueMakeUndefined(context);
+ return;
+ webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
gfloat currentZoom = webkit_web_view_get_zoom_level(view);
webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio);
+}
+static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ zoomIn(FALSE);
return JSValueMakeUndefined(context);
}
-static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
- WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
- if (!view)
- return JSValueMakeUndefined(context);
+ zoomOut(FALSE);
+ return JSValueMakeUndefined(context);
+}
- webkit_web_view_zoom_in(view);
+static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ zoomIn(TRUE);
return JSValueMakeUndefined(context);
}
static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
- WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
- if (!view)
- return JSValueMakeUndefined(context);
-
- webkit_web_view_zoom_out(view);
+ zoomOut(TRUE);
return JSValueMakeUndefined(context);
}
diff --git a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
index 668b852..58d2631 100644
--- a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
@@ -56,7 +56,9 @@ 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);
int webkit_web_frame_page_number_for_element_by_id(WebKitWebFrame* frame, const gchar* id, float pageWidth, float pageHeight);
+int webkit_web_frame_number_of_pages(WebKitWebFrame* frame, float pageWidth, float pageHeight);
void webkit_web_inspector_execute_script(WebKitWebInspector* inspector, long callId, const gchar* script);
+gchar* webkit_web_frame_marker_text_for_list_item(WebKitWebFrame* frame, JSContextRef context, JSValueRef nodeObject);
}
static gchar* copyWebSettingKey(gchar* preferenceKey)
@@ -65,12 +67,13 @@ static gchar* copyWebSettingKey(gchar* preferenceKey)
if (!keyTable) {
// If you add a pref here, make sure you reset the value in
- // DumpRenderTree::resetWebViewToConsistentStateBeforeTesting.
+ // DumpRenderTree::resetDefaultsToConsistentValues.
keyTable = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(keyTable, g_strdup("WebKitJavaScriptEnabled"), g_strdup("enable-scripts"));
g_hash_table_insert(keyTable, g_strdup("WebKitDefaultFontSize"), g_strdup("default-font-size"));
g_hash_table_insert(keyTable, g_strdup("WebKitEnableCaretBrowsing"), g_strdup("enable-caret-browsing"));
g_hash_table_insert(keyTable, g_strdup("WebKitUsesPageCachePreferenceKey"), g_strdup("enable-page-cache"));
+ g_hash_table_insert(keyTable, g_strdup("WebKitPluginsEnabled"), g_strdup("enable-plugins"));
}
return g_strdup(static_cast<gchar*>(g_hash_table_lookup(keyTable, preferenceKey)));
@@ -141,6 +144,19 @@ void LayoutTestController::keepWebHistory()
// FIXME: implement
}
+JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value)
+{
+ // FIXME: Implement this.
+ return JSValueMakeUndefined(context);
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
+{
+ // FIXME: implement
+ JSRetainPtr<JSStringRef> string(Adopt, JSStringCreateWithUTF8CString(""));
+ return string;
+}
+
int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidth, float pageHeight)
{
gchar* idGChar = JSStringCopyUTF8CString(id);
@@ -149,10 +165,9 @@ int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWid
return pageNumber;
}
-int LayoutTestController::numberOfPages(float, float)
+int LayoutTestController::numberOfPages(float pageWidth, float pageHeight)
{
- // FIXME: implement
- return -1;
+ return webkit_web_frame_number_of_pages(mainFrame, pageWidth, pageHeight);
}
size_t LayoutTestController::webHistoryItemCount()
@@ -210,7 +225,19 @@ void LayoutTestController::setAcceptsEditing(bool acceptsEditing)
void LayoutTestController::setAlwaysAcceptCookies(bool alwaysAcceptCookies)
{
- // FIXME: Implement this (and restore the default value before running each test in DumpRenderTree.cpp).
+#ifdef HAVE_LIBSOUP_2_29_90
+ SoupSession* session = webkit_get_default_session();
+ SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
+
+ SoupCookieJarAcceptPolicy policy;
+
+ if (alwaysAcceptCookies)
+ policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+ else
+ policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
+
+ g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, policy, NULL);
+#endif
}
void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive)
@@ -224,7 +251,12 @@ void LayoutTestController::waitForPolicyDelegate()
setWaitToDump(true);
}
-void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains)
+void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
+{
+ // FIXME: implement
+}
+
+void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains)
{
gchar* sourceOriginGChar = JSStringCopyUTF8CString(sourceOrigin);
gchar* protocolGChar = JSStringCopyUTF8CString(protocol);
@@ -235,6 +267,11 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J
g_free(hostGChar);
}
+void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains)
+{
+ // FIXME: implement
+}
+
void LayoutTestController::setMainFrameIsFirstResponder(bool flag)
{
// FIXME: implement
@@ -303,7 +340,7 @@ static gboolean waitToDumpWatchdogFired(void*)
void LayoutTestController::setWaitToDump(bool waitUntilDone)
{
- static const int timeoutSeconds = 15;
+ static const int timeoutSeconds = 30;
m_waitToDump = waitUntilDone;
if (m_waitToDump && !waitToDumpWatchdog)
@@ -334,11 +371,20 @@ void LayoutTestController::setXSSAuditorEnabled(bool flag)
g_object_set(G_OBJECT(settings), "enable-xss-auditor", flag, NULL);
}
-void LayoutTestController::setFrameSetFlatteningEnabled(bool flag)
+void LayoutTestController::setFrameFlatteningEnabled(bool flag)
{
// FIXME: implement
}
+void LayoutTestController::setSpatialNavigationEnabled(bool flag)
+{
+ WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
+ ASSERT(view);
+
+ WebKitWebSettings* settings = webkit_web_view_get_settings(view);
+ g_object_set(G_OBJECT(settings), "enable-spatial-navigation", flag, NULL);
+}
+
void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool flag)
{
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
@@ -390,8 +436,7 @@ void LayoutTestController::setJavaScriptProfilingEnabled(bool flag)
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
ASSERT(view);
- WebKitWebSettings* settings = webkit_web_view_get_settings(view);
- g_object_set(G_OBJECT(settings), "enable-developer-extras", flag, NULL);
+ setDeveloperExtrasEnabled(flag);
WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", flag, NULL);
@@ -556,24 +601,28 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source)
printf("LayoutTestController::addUserStyleSheet not implemented.\n");
}
-void LayoutTestController::showWebInspector()
+void LayoutTestController::setDeveloperExtrasEnabled(bool enabled)
{
WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame);
WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView);
+
+ g_object_set(webSettings, "enable-developer-extras", enabled, NULL);
+}
+
+void LayoutTestController::showWebInspector()
+{
+ WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame);
WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
- g_object_set(webSettings, "enable-developer-extras", TRUE, NULL);
webkit_web_inspector_show(inspector);
}
void LayoutTestController::closeWebInspector()
{
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)
@@ -596,7 +645,37 @@ void LayoutTestController::removeAllVisitedLinks()
// FIXME: Implement this.
}
+bool LayoutTestController::callShouldCloseOnWebView()
+{
+ // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27481
+ return false;
+}
+
void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL)
{
}
+
+void LayoutTestController::apiTestGoToCurrentBackForwardItem()
+{
+
+}
+
+void LayoutTestController::setWebViewEditable(bool)
+{
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const
+{
+ gchar* markerTextGChar = webkit_web_frame_marker_text_for_list_item(mainFrame, context, nodeObject);
+ if (!markerTextGChar)
+ return 0;
+
+ JSRetainPtr<JSStringRef> markerText(Adopt, JSStringCreateWithUTF8CString(markerTextGChar));
+ g_free(markerTextGChar);
+ return markerText;
+}
+
+void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef)
+{
+}
diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm
index 4d2da6e..9d7edef 100644
--- a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm
+++ b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm
@@ -40,6 +40,12 @@ AccessibilityController::~AccessibilityController()
{
}
+AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y)
+{
+ id accessibilityObject = [[[mainFrame frameView] documentView] accessibilityHitTest:NSMakePoint(x, y)];
+ return AccessibilityUIElement(accessibilityObject);
+}
+
AccessibilityUIElement AccessibilityController::focusedElement()
{
// FIXME: we could do some caching here.
diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
index e9361f2..a39dabb 100644
--- a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
+++ b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
@@ -57,32 +57,11 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification,
@interface NSObject (WebKitAccessibilityAdditions)
- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
-- (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context;
+- (void)accessibilitySetShouldRepostNotifications:(BOOL)repost;
- (NSUInteger)accessibilityIndexOfChild:(id)child;
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
@end
-AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
- : m_element(element)
- , m_notificationFunctionCallback(0)
-{
- [m_element retain];
-}
-
-AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
- : m_element(other.m_element)
- , m_notificationFunctionCallback(0)
-{
- [m_element retain];
-}
-
-AccessibilityUIElement::~AccessibilityUIElement()
-{
- // Make sure that our notification callback does not stick around.
- if (m_notificationFunctionCallback)
- [m_element accessibilitySetPostedNotificationCallback:0 withContext:0];
- [m_element release];
-}
-
@interface NSString (JSStringRefAdditions)
+ (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef;
- (JSStringRef)createJSStringRef;
@@ -106,6 +85,88 @@ AccessibilityUIElement::~AccessibilityUIElement()
@end
+@interface AccessibilityNotificationHandler : NSObject
+{
+ id m_platformElement;
+ JSObjectRef m_notificationFunctionCallback;
+}
+
+@end
+
+@implementation AccessibilityNotificationHandler
+
+- (id)initWithPlatformElement:(id)platformElement
+{
+ self = [super init];
+
+ m_platformElement = platformElement;
+
+ // Once an object starts requesting notifications, it's on for the duration of the program.
+ // This is to avoid any race conditions between tests turning this flag on and off. Instead
+ // AccessibilityNotificationHandler can just listen when they want to.
+ [m_platformElement accessibilitySetShouldRepostNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_notificationReceived:) name:@"AXDRTNotification" object:nil];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback);
+ m_notificationFunctionCallback = 0;
+
+ [super dealloc];
+}
+
+- (void)_notificationReceived:(NSNotification *)notification
+{
+ NSString *notificationName = [[notification userInfo] objectForKey:@"notificationName"];
+ if (!notificationName)
+ return;
+
+ JSRetainPtr<JSStringRef> jsNotification(Adopt, [notificationName createJSStringRef]);
+ JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get());
+ JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 1, &argument, 0);
+}
+
+- (void)setCallback:(JSObjectRef)callback
+{
+ if (!callback)
+ return;
+
+ // Release the old callback.
+ if (m_notificationFunctionCallback)
+ JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback);
+
+ m_notificationFunctionCallback = callback;
+ JSValueProtect([mainFrame globalContext], m_notificationFunctionCallback);
+}
+
+@end
+
+AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
+ : m_element(element)
+ , m_notificationHandler(0)
+{
+ // FIXME: ap@webkit.org says ObjC objects need to be CFRetained/CFRelease to be GC-compliant on the mac.
+ [m_element retain];
+}
+
+AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
+ : m_element(other.m_element)
+ , m_notificationHandler(0)
+{
+ [m_element retain];
+}
+
+AccessibilityUIElement::~AccessibilityUIElement()
+{
+ // The notification handler should be nil because removeNotificationListener() should have been called in the test.
+ ASSERT(!m_notificationHandler);
+ [m_element release];
+}
+
static NSString* descriptionOfValue(id valueObject, id focusedAccessibilityObject)
{
if (!valueObject)
@@ -439,6 +500,12 @@ JSStringRef AccessibilityUIElement::language()
return concatenateAttributeAndValue(@"AXLanguage", description);
}
+JSStringRef AccessibilityUIElement::helpText() const
+{
+ id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityHelpAttribute], m_element);
+ return concatenateAttributeAndValue(@"AXHelp", description);
+}
+
double AccessibilityUIElement::x()
{
NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute];
@@ -676,6 +743,16 @@ JSStringRef AccessibilityUIElement::attributesOfHeader()
return descriptionOfElements(headerVector);
}
+int AccessibilityUIElement::rowCount()
+{
+ return [m_element accessibilityArrayAttributeCount:NSAccessibilityRowsAttribute];
+}
+
+int AccessibilityUIElement::columnCount()
+{
+ return [m_element accessibilityArrayAttributeCount:NSAccessibilityColumnsAttribute];
+}
+
int AccessibilityUIElement::indexInTable()
{
NSNumber* indexNumber = [m_element accessibilityAttributeValue:NSAccessibilityIndexAttribute];
@@ -736,6 +813,11 @@ void AccessibilityUIElement::showMenu()
[m_element accessibilityPerformAction:NSAccessibilityShowMenuAction];
}
+void AccessibilityUIElement::press()
+{
+ [m_element accessibilityPerformAction:NSAccessibilityPressAction];
+}
+
JSStringRef AccessibilityUIElement::accessibilityValue() const
{
// FIXME: implement
@@ -758,28 +840,30 @@ JSStringRef AccessibilityUIElement::url()
return [[url absoluteString] createJSStringRef];
}
-static void _accessibilityNotificationCallback(id element, NSString* notification, void* context)
-{
- if (!context)
- return;
-
- JSObjectRef functionCallback = static_cast<JSObjectRef>(context);
-
- JSRetainPtr<JSStringRef> jsNotification(Adopt, [notification createJSStringRef]);
- JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get());
- JSObjectCallAsFunction([mainFrame globalContext], functionCallback, NULL, 1, &argument, NULL);
-}
-
bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
{
if (!functionCallback)
return false;
- m_notificationFunctionCallback = functionCallback;
- [platformUIElement() accessibilitySetPostedNotificationCallback:_accessibilityNotificationCallback withContext:reinterpret_cast<void*>(m_notificationFunctionCallback)];
+ // Mac programmers should not be adding more than one notification listener per element.
+ // Other platforms may be different.
+ if (m_notificationHandler)
+ return false;
+ m_notificationHandler = [[AccessibilityNotificationHandler alloc] initWithPlatformElement:platformUIElement()];
+ [m_notificationHandler setCallback:functionCallback];
+
return true;
}
+void AccessibilityUIElement::removeNotificationListener()
+{
+ // Mac programmers should not be trying to remove a listener that's already removed.
+ ASSERT(m_notificationHandler);
+
+ [m_notificationHandler release];
+ m_notificationHandler = nil;
+}
+
bool AccessibilityUIElement::isSelectable() const
{
// FIXME: implement
@@ -812,7 +896,9 @@ bool AccessibilityUIElement::isCollapsed() const
bool AccessibilityUIElement::hasPopup() const
{
- // FIXME: implement
+ id value = [m_element accessibilityAttributeValue:@"AXHasPopup"];
+ if ([value isKindOfClass:[NSNumber class]])
+ return [value boolValue];
return false;
}
diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig
index a72dd7d..5f989e0 100644
--- a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig
+++ b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig
@@ -34,3 +34,35 @@ GCC_WARN_UNUSED_VARIABLE = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
WARNING_CFLAGS = -Wall -W -Wno-unused-parameter
LINKER_DISPLAYS_MANGLED_NAMES = YES;
+
+
+TARGET_MAC_OS_X_VERSION_MAJOR = $(MAC_OS_X_VERSION_MAJOR);
+
+
+// Use GCC 4.2 with Xcode 3.1, which includes GCC 4.2 but defaults to GCC 4.0.
+// Note that Xcode versions as new as 3.1.2 use XCODE_VERSION_ACTUAL for the minor version
+// number. Newer versions of Xcode use XCODE_VERSION_MINOR for the minor version, and
+// XCODE_VERSION_ACTUAL for the full version number.
+TARGET_GCC_VERSION = $(TARGET_GCC_VERSION_$(TARGET_MAC_OS_X_VERSION_MAJOR));
+TARGET_GCC_VERSION_ = $(TARGET_GCC_VERSION_1040);
+TARGET_GCC_VERSION_1040 = GCC_40;
+TARGET_GCC_VERSION_1050 = $(TARGET_GCC_VERSION_1050_$(XCODE_VERSION_MINOR));
+TARGET_GCC_VERSION_1050_ = $(TARGET_GCC_VERSION_1050_$(XCODE_VERSION_ACTUAL));
+TARGET_GCC_VERSION_1050_0310 = GCC_42;
+TARGET_GCC_VERSION_1050_0320 = GCC_42;
+TARGET_GCC_VERSION_1060 = GCC_42;
+TARGET_GCC_VERSION_1070 = LLVM_GCC_42;
+
+GCC_VERSION = $(GCC_VERSION_$(TARGET_GCC_VERSION));
+GCC_VERSION_GCC_40 = 4.0;
+GCC_VERSION_GCC_42 = 4.2;
+GCC_VERSION_LLVM_GCC_42 = com.apple.compilers.llvmgcc42;
+
+// If the target Mac OS X version does not match the current Mac OS X version then we'll want to build using the target version's SDK.
+SDKROOT = $(SDKROOT_$(MAC_OS_X_VERSION_MAJOR)_$(TARGET_MAC_OS_X_VERSION_MAJOR));
+SDKROOT_1050_1040 = macosx10.4;
+SDKROOT_1060_1040 = macosx10.4;
+SDKROOT_1060_1050 = macosx10.5;
+SDKROOT_1070_1040 = macosx10.4;
+SDKROOT_1070_1050 = macosx10.5;
+SDKROOT_1070_1060 = macosx10.6;
diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig
index 96a39a9..ab3278e 100644
--- a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig
+++ b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig
@@ -23,7 +23,7 @@
#include "Base.xcconfig"
-ARCHS = $(ARCHS_$(MAC_OS_X_VERSION_MAJOR));
+ARCHS = $(ARCHS_$(TARGET_MAC_OS_X_VERSION_MAJOR));
ARCHS_ = $(ARCHS_1040);
ARCHS_1040 = $(NATIVE_ARCH);
ARCHS_1050 = $(NATIVE_ARCH);
@@ -32,7 +32,7 @@ ARCHS_1070 = $(ARCHS_STANDARD_32_64_BIT);
ONLY_ACTIVE_ARCH = YES;
-MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(MAC_OS_X_VERSION_MAJOR))
+MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(TARGET_MAC_OS_X_VERSION_MAJOR))
MACOSX_DEPLOYMENT_TARGET_ = 10.4;
MACOSX_DEPLOYMENT_TARGET_1040 = 10.4;
MACOSX_DEPLOYMENT_TARGET_1050 = 10.5;
diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm
index c7ddf21..0210cf0 100644
--- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm
+++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm
@@ -76,15 +76,21 @@
#import <WebKit/WebTypesInternal.h>
#import <WebKit/WebViewPrivate.h>
#import <getopt.h>
-#import <mach-o/getsect.h>
#import <objc/objc-runtime.h>
#import <wtf/Assertions.h>
#import <wtf/RetainPtr.h>
#import <wtf/Threading.h>
#import <wtf/OwnPtr.h>
+extern "C" {
+#import <mach-o/getsect.h>
+}
+
using namespace std;
+@interface DumpRenderTreeApplication : NSApplication
+@end
+
@interface DumpRenderTreeEvent : NSEvent
@end
@@ -246,6 +252,7 @@ static void activateFonts()
static const char* fontFileNames[] = {
"AHEM____.TTF",
+ "ColorBits.ttf",
"WebKitWeightWatcher100.ttf",
"WebKitWeightWatcher200.ttf",
"WebKitWeightWatcher300.ttf",
@@ -355,7 +362,14 @@ void testStringByEvaluatingJavaScriptFromString()
static NSString *libraryPathForDumpRenderTree()
{
- return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath];
+ //FIXME: This may not be sufficient to prevent interactions/crashes
+ //when running more than one copy of DumpRenderTree.
+ //See https://bugs.webkit.org/show_bug.cgi?id=10906
+ char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
+ if (dumpRenderTreeTemp)
+ return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:dumpRenderTreeTemp length:strlen(dumpRenderTreeTemp)];
+ else
+ return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath];
}
// Called before each test.
@@ -420,7 +434,7 @@ static void resetDefaultsToConsistentValues()
[preferences setOfflineWebApplicationCacheEnabled:YES];
[preferences setDeveloperExtrasEnabled:NO];
[preferences setLoadsImagesAutomatically:YES];
- [preferences setFrameSetFlatteningEnabled:NO];
+ [preferences setFrameFlatteningEnabled:NO];
if (persistentUserStyleSheetLocation) {
[preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]];
[preferences setUserStyleSheetEnabled:YES];
@@ -430,20 +444,8 @@ static void resetDefaultsToConsistentValues()
// The back/forward cache is causing problems due to layouts during transition from one page to another.
// So, turn it off for now, but we might want to turn it back on some day.
[preferences setUsesPageCache:NO];
-
-#if defined(BUILDING_ON_LEOPARD)
- // Disable hardware composititing to avoid timeouts and crashes from buggy CoreVideo teardown code.
- // https://bugs.webkit.org/show_bug.cgi?id=28845 and rdar://problem/7228836
- SInt32 qtVersion;
- OSErr err = Gestalt(gestaltQuickTimeVersion, &qtVersion);
- assert(err == noErr);
- // 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 <= 0x07648000) // 7.6.4, final release (0x8). See http://developer.apple.com/mac/library/techn
- [preferences setAcceleratedCompositingEnabled:NO];
- else
-#endif
- [preferences setAcceleratedCompositingEnabled:YES];
+ [preferences setAcceleratedCompositingEnabled:YES];
+ [preferences setWebGLEnabled:NO];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain];
@@ -686,7 +688,7 @@ void dumpRenderTree(int argc, const char *argv[])
int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [NSApplication sharedApplication]; // Force AppKit to init itself
+ [DumpRenderTreeApplication sharedApplication]; // Force AppKit to init itself
dumpRenderTree(argc, argv);
[WebCoreStatistics garbageCollectJavaScriptObjects];
[WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts
@@ -1139,9 +1141,15 @@ static bool shouldOpenWebInspector(const char* pathOrURL)
return strstr(pathOrURL, "inspector/");
}
+static bool shouldEnableDeveloperExtras(const char* pathOrURL)
+{
+ return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "inspector-enabled/");
+}
+
static void resetWebViewToConsistentStateBeforeTesting()
{
WebView *webView = [mainFrame webView];
+ [webView setEditable:NO];
[(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES];
[webView makeTextStandardSize:nil];
[webView resetPageZoom:nil];
@@ -1162,7 +1170,7 @@ static void resetWebViewToConsistentStateBeforeTesting()
[[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:NO];
[WebView _setUsesTestModeFocusRingColor:YES];
- [WebView _resetOriginAccessWhiteLists];
+ [WebView _resetOriginAccessWhitelists];
}
static void runTest(const string& testPathOrURL)
@@ -1217,8 +1225,11 @@ static void runTest(const string& testPathOrURL)
else
[[mainFrame webView] setHistoryDelegate:nil];
- if (shouldOpenWebInspector(pathOrURL.c_str()))
- gLayoutTestController->showWebInspector();
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
+ gLayoutTestController->setDeveloperExtrasEnabled(true);
+ if (shouldOpenWebInspector(pathOrURL.c_str()))
+ gLayoutTestController->showWebInspector();
+ }
if ([WebHistory optionalSharedHistory])
[WebHistory setOptionalSharedHistory:nil];
@@ -1238,9 +1249,10 @@ static void runTest(const string& testPathOrURL)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
[pool release];
+
while (!done) {
pool = [[NSAutoreleasePool alloc] init];
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
[pool release];
}
@@ -1268,7 +1280,8 @@ static void runTest(const string& testPathOrURL)
}
}
- if (shouldOpenWebInspector(pathOrURL.c_str()))
+ // If developer extras enabled Web Inspector may have been open by the test.
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str()))
gLayoutTestController->closeWebInspector();
resetWebViewToConsistentStateBeforeTesting();
@@ -1307,3 +1320,13 @@ void displayWebView()
}
@end
+
+@implementation DumpRenderTreeApplication
+
+- (BOOL)isRunning
+{
+ // <rdar://problem/7686123> Java plug-in freezes unless NSApplication is running
+ return YES;
+}
+
+@end
diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm
index feaeddc..8c5cebf 100644
--- a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm
+++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm
@@ -134,7 +134,9 @@ BOOL replayingSavedEvents;
|| aSelector == @selector(textZoomIn)
|| aSelector == @selector(textZoomOut)
|| aSelector == @selector(zoomPageIn)
- || aSelector == @selector(zoomPageOut))
+ || aSelector == @selector(zoomPageOut)
+ || aSelector == @selector(mouseScrollByX:andY:)
+ || aSelector == @selector(continuousMouseScrollByX:andY:))
return NO;
return YES;
}
@@ -166,6 +168,10 @@ BOOL replayingSavedEvents;
return @"mouseMoveTo";
if (aSelector == @selector(setDragMode:))
return @"setDragMode";
+ if (aSelector == @selector(mouseScrollByX:andY:))
+ return @"mouseScrollBy";
+ if (aSelector == @selector(continuousMouseScrollByX:andY:))
+ return @"continuousMouseScrollBy";
return nil;
}
@@ -453,6 +459,39 @@ static int buildModifierFlags(const WebScriptObject* modifiers)
}
}
+- (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)c
+{
+ // CGEventCreateScrollWheelEvent() was introduced in 10.5
+#if !defined(BUILDING_ON_TIGER)
+ CGScrollEventUnit unit = c?kCGScrollEventUnitPixel:kCGScrollEventUnitLine;
+ CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x);
+
+ // CGEvent locations are in global display coordinates.
+ CGPoint lastGlobalMousePosition = {
+ lastMousePosition.x,
+ [[NSScreen mainScreen] frame].size.height - lastMousePosition.y
+ };
+ CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
+
+ NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
+ CFRelease(cgScrollEvent);
+
+ NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]];
+ if (subView)
+ [subView scrollWheel:scrollEvent];
+#endif
+}
+
+- (void)continuousMouseScrollByX:(int)x andY:(int)y
+{
+ [self mouseScrollByX:x andY:y continuously:YES];
+}
+
+- (void)mouseScrollByX:(int)x andY:(int)y
+{
+ [self mouseScrollByX:x andY:y continuously:NO];
+}
+
- (void)contextClick
{
[[[mainFrame frameView] documentView] layout];
@@ -507,33 +546,43 @@ static int buildModifierFlags(const WebScriptObject* modifiers)
- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation
{
NSString *eventCharacter = character;
+ unsigned short keyCode = 0;
if ([character isEqualToString:@"leftArrow"]) {
const unichar ch = NSLeftArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x7B;
} else if ([character isEqualToString:@"rightArrow"]) {
const unichar ch = NSRightArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x7C;
} else if ([character isEqualToString:@"upArrow"]) {
const unichar ch = NSUpArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x7E;
} else if ([character isEqualToString:@"downArrow"]) {
const unichar ch = NSDownArrowFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x7D;
} else if ([character isEqualToString:@"pageUp"]) {
const unichar ch = NSPageUpFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x74;
} else if ([character isEqualToString:@"pageDown"]) {
const unichar ch = NSPageDownFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x79;
} else if ([character isEqualToString:@"home"]) {
const unichar ch = NSHomeFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x73;
} else if ([character isEqualToString:@"end"]) {
const unichar ch = NSEndFunctionKey;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x77;
} else if ([character isEqualToString:@"delete"]) {
const unichar ch = 0x7f;
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ keyCode = 0x75;
}
// Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
@@ -542,9 +591,59 @@ static int buildModifierFlags(const WebScriptObject* modifiers)
if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
const unichar ch = NSF1FunctionKey + (i - 1);
eventCharacter = [NSString stringWithCharacters:&ch length:1];
+ switch (i) {
+ case 1: keyCode = 0x7A; break;
+ case 2: keyCode = 0x78; break;
+ case 3: keyCode = 0x63; break;
+ case 4: keyCode = 0x76; break;
+ case 5: keyCode = 0x60; break;
+ case 6: keyCode = 0x61; break;
+ case 7: keyCode = 0x62; break;
+ case 8: keyCode = 0x64; break;
+ case 9: keyCode = 0x65; break;
+ case 10: keyCode = 0x6D; break;
+ case 11: keyCode = 0x67; break;
+ case 12: keyCode = 0x6F; break;
+ case 13: keyCode = 0x69; break;
+ case 14: keyCode = 0x6B; break;
+ case 15: keyCode = 0x71; break;
+ case 16: keyCode = 0x6A; break;
+ case 17: keyCode = 0x40; break;
+ case 18: keyCode = 0x4F; break;
+ case 19: keyCode = 0x50; break;
+ case 20: keyCode = 0x5A; break;
+ }
}
}
+ // FIXME: No keyCode is set for most keys.
+ if ([character isEqualToString:@"\t"])
+ keyCode = 0x30;
+ else if ([character isEqualToString:@" "])
+ keyCode = 0x31;
+ else if ([character isEqualToString:@"\r"])
+ keyCode = 0x24;
+ else if ([character isEqualToString:@"\n"])
+ keyCode = 0x4C;
+ else if ([character isEqualToString:@"\x8"])
+ keyCode = 0x33;
+ else if ([character isEqualToString:@"7"])
+ keyCode = 0x1A;
+ else if ([character isEqualToString:@"5"])
+ keyCode = 0x17;
+ else if ([character isEqualToString:@"9"])
+ keyCode = 0x19;
+ else if ([character isEqualToString:@"0"])
+ keyCode = 0x1D;
+ else if ([character isEqualToString:@"a"])
+ keyCode = 0x00;
+ else if ([character isEqualToString:@"b"])
+ keyCode = 0x0B;
+ else if ([character isEqualToString:@"d"])
+ keyCode = 0x02;
+ else if ([character isEqualToString:@"e"])
+ keyCode = 0x0E;
+
NSString *charactersIgnoringModifiers = eventCharacter;
int modifierFlags = 0;
@@ -570,7 +669,7 @@ static int buildModifierFlags(const WebScriptObject* modifiers)
characters:eventCharacter
charactersIgnoringModifiers:charactersIgnoringModifiers
isARepeat:NO
- keyCode:0];
+ keyCode:keyCode];
[[[[mainFrame webView] window] firstResponder] keyDown:event];
@@ -583,7 +682,7 @@ static int buildModifierFlags(const WebScriptObject* modifiers)
characters:eventCharacter
charactersIgnoringModifiers:charactersIgnoringModifiers
isARepeat:NO
- keyCode:0];
+ keyCode:keyCode];
[[[[mainFrame webView] window] firstResponder] keyUp:event];
}
diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
index 66ba5f0..e62e411 100644
--- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
+++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
@@ -115,6 +115,11 @@ void LayoutTestController::addDisallowedURL(JSStringRef url)
CFSetAddValue(disallowedURLs, [request URL]);
}
+bool LayoutTestController::callShouldCloseOnWebView()
+{
+ return [[mainFrame webView] shouldClose];
+}
+
void LayoutTestController::clearAllDatabases()
{
[[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases];
@@ -176,6 +181,23 @@ void LayoutTestController::keepWebHistory()
}
}
+JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value)
+{
+ return [[mainFrame webView] _computedStyleIncludingVisitedInfo:context forElement:value];
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
+{
+ JSRetainPtr<JSStringRef> string(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame _layerTreeAsText]));
+ return string;
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const
+{
+ // FIXME: Implement me.
+ return JSRetainPtr<JSStringRef>();
+}
+
int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidthInPixels, float pageHeightInPixels)
{
RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id));
@@ -302,7 +324,7 @@ void LayoutTestController::setIconDatabaseEnabled(bool iconDatabaseEnabled)
void LayoutTestController::setJavaScriptProfilingEnabled(bool profilingEnabled)
{
- [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:profilingEnabled];
+ setDeveloperExtrasEnabled(profilingEnabled);
[[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:profilingEnabled];
}
@@ -324,9 +346,14 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled)
[[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled];
}
-void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
+void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
{
- [[[mainFrame webView] preferences] setFrameSetFlatteningEnabled:enabled];
+ [[[mainFrame webView] preferences] setFrameFlatteningEnabled:enabled];
+}
+
+void LayoutTestController::setSpatialNavigationEnabled(bool enabled)
+{
+ // FIXME: Implement for SpatialNavigation layout tests.
}
void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
@@ -424,7 +451,7 @@ void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag)
[[mainFrame webView] setSelectTrailingWhitespaceEnabled:flag];
}
-static const CFTimeInterval waitToDumpWatchdogInterval = 15.0;
+static const CFTimeInterval waitToDumpWatchdogInterval = 30.0;
static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
{
@@ -541,7 +568,7 @@ void LayoutTestController::waitForPolicyDelegate()
[[mainFrame webView] setPolicyDelegate:policyDelegate];
}
-void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
{
RetainPtr<CFStringRef> sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
@@ -549,7 +576,23 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J
NSString *destinationProtocolNS = (NSString *)protocolCF.get();
RetainPtr<CFStringRef> hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
NSString *destinationHostNS = (NSString *)hostCF.get();
- [WebView _whiteListAccessFromOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
+ [WebView _addOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
+}
+
+void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+{
+ RetainPtr<CFStringRef> sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
+ NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
+ RetainPtr<CFStringRef> protocolCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol));
+ NSString *destinationProtocolNS = (NSString *)protocolCF.get();
+ RetainPtr<CFStringRef> hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
+ NSString *destinationHostNS = (NSString *)hostCF.get();
+ [WebView _removeOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
+}
+
+void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
+{
+ // FIXME: implement
}
void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
@@ -566,16 +609,19 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source)
[WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil];
}
+void LayoutTestController::setDeveloperExtrasEnabled(bool enabled)
+{
+ [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:enabled];
+}
+
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)
@@ -686,3 +732,101 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data,
[delegate release];
[pool release];
}
+
+void LayoutTestController::apiTestGoToCurrentBackForwardItem()
+{
+ WebView *view = [mainFrame webView];
+ [view goToBackForwardItem:[[view backForwardList] currentItem]];
+}
+
+void LayoutTestController::setWebViewEditable(bool editable)
+{
+ WebView *view = [mainFrame webView];
+ [view setEditable:editable];
+}
+
+#ifndef BUILDING_ON_TIGER
+static NSString *SynchronousLoaderRunLoopMode = @"DumpRenderTreeSynchronousLoaderRunLoopMode";
+
+@interface SynchronousLoader : NSObject
+{
+ NSString *m_username;
+ NSString *m_password;
+ BOOL m_isDone;
+}
++ (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password;
+@end
+
+@implementation SynchronousLoader : NSObject
+- (void)dealloc
+{
+ [m_username release];
+ [m_password release];
+
+ [super dealloc];
+}
+
+- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
+{
+ return YES;
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
+{
+ if ([challenge previousFailureCount] == 0) {
+ NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_username password:m_password persistence:NSURLCredentialPersistenceForSession];
+ [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
+ return;
+ }
+ [[challenge sender] cancelAuthenticationChallenge:challenge];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
+{
+ printf("SynchronousLoader failed: %s\n", [[error description] UTF8String]);
+ m_isDone = YES;
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
+{
+ m_isDone = YES;
+}
+
++ (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password
+{
+ ASSERT(![[request URL] user]);
+ ASSERT(![[request URL] password]);
+
+ SynchronousLoader *delegate = [[SynchronousLoader alloc] init];
+ delegate->m_username = [username copy];
+ delegate->m_password = [password copy];
+
+ NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
+ [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:SynchronousLoaderRunLoopMode];
+ [connection start];
+
+ while (!delegate->m_isDone)
+ [[NSRunLoop currentRunLoop] runMode:SynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
+
+ [connection cancel];
+
+ [connection release];
+ [delegate release];
+}
+
+@end
+#endif
+
+void LayoutTestController::authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password)
+{
+ // See <rdar://problem/7880699>.
+#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ RetainPtr<CFStringRef> urlStringCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
+ RetainPtr<CFStringRef> usernameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, username));
+ RetainPtr<CFStringRef> passwordCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, password));
+
+ NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:(NSString *)urlStringCF.get()]];
+
+ [SynchronousLoader makeRequest:request withUsername:(NSString *)usernameCF.get() password:(NSString *)passwordCF.get()];
+#endif
+}
diff --git a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm
index 6f82e01..9244110 100644
--- a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm
+++ b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm
@@ -35,6 +35,8 @@
#import <WebKit/WebTypesInternal.h>
#import <wtf/Assertions.h>
+using namespace std;
+
@interface NSURL (DRTExtras)
- (NSString *)_drt_descriptionSuitableForTestResult;
@end
@@ -121,10 +123,10 @@
return @"<unknown>";
}
--(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource
+-(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource
{
if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) {
- NSString *string = [NSString stringWithFormat:@"%@ - willSendRequest %@ redirectResponse %@", identifier, [newRequest _drt_descriptionSuitableForTestResult],
+ NSString *string = [NSString stringWithFormat:@"%@ - willSendRequest %@ redirectResponse %@", identifier, [request _drt_descriptionSuitableForTestResult],
[redirectResponse _drt_descriptionSuitableForTestResult]];
printf("%s\n", [string UTF8String]);
}
@@ -137,7 +139,7 @@
return nil;
}
- NSURL *url = [newRequest URL];
+ NSURL *url = [request URL];
NSString *host = [url host];
if (host
&& (NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"http"] || NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"https"])
@@ -151,7 +153,15 @@
if (disallowedURLs && CFSetContainsValue(disallowedURLs, url))
return nil;
- return newRequest;
+ NSMutableURLRequest *newRequest = [request mutableCopy];
+ const set<string>& clearHeaders = gLayoutTestController->willSendRequestClearHeaders();
+ for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) {
+ NSString *nsHeader = [[NSString alloc] initWithUTF8String:header->c_str()];
+ [newRequest setValue:nil forHTTPHeaderField:nsHeader];
+ [nsHeader release];
+ }
+
+ return [newRequest autorelease];
}
- (void)webView:(WebView *)wv resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource
diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro
index ad42bdd..b66eb5d 100644
--- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro
+++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro
@@ -2,12 +2,8 @@ TARGET = DumpRenderTree
CONFIG -= app_bundle
CONFIG += uitools
-mac:!static:contains(QT_CONFIG, qt_framework):!CONFIG(webkit_no_framework) {
- CONFIG -= debug
- CONFIG += release
-}
-
BASEDIR = $$PWD/../
+isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../..
include(../../../WebKit.pri)
INCLUDEPATH += /usr/include/freetype2
@@ -17,7 +13,7 @@ INCLUDEPATH += ../../../JavaScriptCore/ForwardingHeaders
INCLUDEPATH += $$BASEDIR
DESTDIR = ../../../bin
-!win32 {
+!win32:!symbian {
CONFIG += link_pkgconfig
PKGCONFIG += fontconfig
}
diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp
index 43f1318..2c2db92 100644
--- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp
+++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp
@@ -32,6 +32,7 @@
#include "config.h"
#include "DumpRenderTreeQt.h"
+#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h"
#include "EventSenderQt.h"
#include "GCControllerQt.h"
#include "LayoutTestControllerQt.h"
@@ -39,12 +40,11 @@
#include "testplugin.h"
#include "WorkQueue.h"
+#include <QApplication>
#include <QBuffer>
#include <QCryptographicHash>
#include <QDir>
#include <QFile>
-#include <QApplication>
-#include <QUrl>
#include <QFileInfo>
#include <QFocusEvent>
#include <QFontDatabase>
@@ -52,7 +52,13 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
+#include <QPaintDevice>
+#include <QPaintEngine>
+#ifndef QT_NO_PRINTER
+#include <QPrinter>
+#endif
#include <QUndoStack>
+#include <QUrl>
#include <qwebsettings.h>
#include <qwebsecurityorigin.h>
@@ -66,6 +72,7 @@
#endif
#include <limits.h>
+#include <locale.h>
#ifndef Q_OS_WIN
#include <unistd.h>
@@ -73,20 +80,12 @@
#include <qdebug.h>
-extern void qt_drt_run(bool b);
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();
-extern bool qt_drt_hasDocumentElement(QWebFrame* qFrame);
+extern void qt_dump_resource_load_callbacks(bool b);
namespace WebCore {
-// Choose some default values.
-const unsigned int maxViewWidth = 800;
-const unsigned int maxViewHeight = 600;
-
NetworkAccessManager::NetworkAccessManager(QObject* parent)
: QNetworkAccessManager(parent)
{
@@ -116,6 +115,26 @@ void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QLis
}
#endif
+
+#ifndef QT_NO_PRINTER
+class NullPrinter : public QPrinter {
+public:
+ class NullPaintEngine : public QPaintEngine {
+ public:
+ virtual bool begin(QPaintDevice*) { return true; }
+ virtual bool end() { return true; }
+ virtual QPaintEngine::Type type() const { return QPaintEngine::User; }
+ virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { }
+ virtual void updateState(const QPaintEngineState& state) { }
+ };
+
+ virtual QPaintEngine* paintEngine() const { return const_cast<NullPaintEngine*>(&m_engine); }
+
+ NullPaintEngine m_engine;
+};
+#endif
+
+
WebPage::WebPage(QObject* parent, DumpRenderTree* drt)
: QWebPage(parent)
, m_webInspector(0)
@@ -135,6 +154,7 @@ WebPage::WebPage(QObject* parent, DumpRenderTree* drt)
globalSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
globalSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
globalSettings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false);
+ globalSettings->setAttribute(QWebSettings::SpatialNavigationEnabled, false);
connect(this, SIGNAL(geometryChangeRequested(const QRect &)),
this, SLOT(setViewGeometry(const QRect & )));
@@ -165,9 +185,16 @@ void WebPage::resetSettings()
settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows);
settings()->resetAttribute(QWebSettings::JavascriptEnabled);
settings()->resetAttribute(QWebSettings::PrivateBrowsingEnabled);
+ settings()->resetAttribute(QWebSettings::SpatialNavigationEnabled);
settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain);
settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled);
settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls);
+ settings()->resetAttribute(QWebSettings::PluginsEnabled);
+
+ m_drt->layoutTestController()->setCaretBrowsingEnabled(false);
+ m_drt->layoutTestController()->setFrameFlatteningEnabled(false);
+ m_drt->layoutTestController()->setSmartInsertDeleteEnabled(true);
+ m_drt->layoutTestController()->setSelectTrailingWhitespaceEnabled(false);
// globalSettings must be reset explicitly.
m_drt->layoutTestController()->setXSSAuditorEnabled(false);
@@ -315,16 +342,25 @@ DumpRenderTree::DumpRenderTree()
, m_enableTextOutput(false)
, m_singleFileMode(false)
{
- qt_drt_overwritePluginDirectories();
- QWebSettings::enablePersistentStorage();
+ DumpRenderTreeSupportQt::overwritePluginDirectories();
+
+ char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
+ if (dumpRenderTreeTemp)
+ QWebSettings::enablePersistentStorage(QString(dumpRenderTreeTemp));
+ else
+ QWebSettings::enablePersistentStorage();
// create our primary testing page/view.
m_mainView = new QWebView(0);
- m_mainView->resize(QSize(maxViewWidth, maxViewHeight));
+ m_mainView->resize(QSize(LayoutTestController::maxViewWidth, LayoutTestController::maxViewHeight));
m_page = new WebPage(m_mainView, this);
m_mainView->setPage(m_page);
m_mainView->setContextMenuPolicy(Qt::NoContextMenu);
+ // clean up cache by resetting quota.
+ qint64 quota = webPage()->settings()->offlineWebApplicationCacheQuota();
+ webPage()->settings()->setOfflineWebApplicationCacheQuota(quota);
+
// create our controllers. This has to be done before connectFrame,
// as it exports there to the JavaScript DOM window.
m_controller = new LayoutTestController(this);
@@ -348,6 +384,7 @@ DumpRenderTree::DumpRenderTree()
connect(m_page, SIGNAL(loadStarted()),
m_controller, SLOT(resetLoadFinished()));
connect(m_page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
+ connect(m_page, SIGNAL(printRequested(QWebFrame*)), this, SLOT(dryRunPrint(QWebFrame*)));
connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)),
SLOT(titleChanged(const QString&)));
@@ -357,7 +394,8 @@ DumpRenderTree::DumpRenderTree()
this, SLOT(statusBarMessage(const QString&)));
QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection);
- qt_drt_run(true);
+
+ DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true);
QFocusEvent event(QEvent::FocusIn, Qt::ActiveWindowFocusReason);
QApplication::sendEvent(m_mainView, &event);
}
@@ -381,6 +419,14 @@ static void clearHistory(QWebPage* page)
history->setMaximumItemCount(itemCount);
}
+void DumpRenderTree::dryRunPrint(QWebFrame* frame)
+{
+#ifndef QT_NO_PRINTER
+ NullPrinter printer;
+ frame->print(&printer);
+#endif
+}
+
void DumpRenderTree::resetToConsistentStateBeforeTesting()
{
// reset so that any current loads are stopped
@@ -395,18 +441,24 @@ void DumpRenderTree::resetToConsistentStateBeforeTesting()
// of the DRT.
m_controller->reset();
+ // reset mouse clicks counter
+ m_eventSender->resetClickCount();
+
closeRemainingWindows();
m_page->resetSettings();
m_page->undoStack()->clear();
m_page->mainFrame()->setZoomFactor(1.0);
clearHistory(m_page);
- qt_drt_clearFrameName(m_page->mainFrame());
+ DumpRenderTreeSupportQt::clearFrameName(m_page->mainFrame());
+
+ m_page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded);
+ m_page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded);
WorkQueue::shared()->clear();
WorkQueue::shared()->setFrozen(false);
- qt_drt_resetOriginAccessWhiteLists();
+ DumpRenderTreeSupportQt::resetOriginAccessWhiteLists();
QLocale::setDefault(QLocale::c());
setlocale(LC_ALL, "");
@@ -419,20 +471,30 @@ static bool isWebInspectorTest(const QUrl& url)
return false;
}
+static bool shouldEnableDeveloperExtras(const QUrl& url)
+{
+ return isWebInspectorTest(url) || url.path().contains("inspector-enabled/");
+}
+
void DumpRenderTree::open(const QUrl& url)
{
resetToConsistentStateBeforeTesting();
- if (isWebInspectorTest(m_page->mainFrame()->url()))
+ if (shouldEnableDeveloperExtras(m_page->mainFrame()->url())) {
layoutTestController()->closeWebInspector();
+ layoutTestController()->setDeveloperExtrasEnabled(false);
+ }
- if (isWebInspectorTest(url))
- layoutTestController()->showWebInspector();
+ if (shouldEnableDeveloperExtras(url)) {
+ layoutTestController()->setDeveloperExtrasEnabled(true);
+ if (isWebInspectorTest(url))
+ layoutTestController()->showWebInspector();
+ }
// W3C SVG tests expect to be 480x360
bool isW3CTest = url.toString().contains("svg/W3C-SVG-1.1");
- int width = isW3CTest ? 480 : maxViewWidth;
- int height = isW3CTest ? 360 : maxViewHeight;
+ int width = isW3CTest ? 480 : LayoutTestController::maxViewWidth;
+ int height = isW3CTest ? 360 : LayoutTestController::maxViewHeight;
m_mainView->resize(QSize(width, height));
m_page->setPreferredContentsSize(QSize());
m_page->setViewportSize(QSize(width, height));
@@ -441,7 +503,9 @@ void DumpRenderTree::open(const QUrl& url)
m_page->event(&ev);
QWebSettings::clearMemoryCaches();
+#if !(defined(Q_WS_S60) && QT_VERSION <= QT_VERSION_CHECK(4, 6, 2))
QFontDatabase::removeAllApplicationFonts();
+#endif
#if defined(Q_WS_X11)
initializeFonts();
#endif
@@ -557,7 +621,7 @@ void DumpRenderTree::hidePage()
QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame)
{
- if (!frame || !qt_drt_hasDocumentElement(frame))
+ if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame))
return QString();
QString result;
@@ -666,8 +730,9 @@ static const char *methodNameStringForFailedTest(LayoutTestController *controlle
void DumpRenderTree::dump()
{
- // Prevent any further frame load callbacks from appearing after we dump the result.
+ // Prevent any further frame load or resource load callbacks from appearing after we dump the result.
qt_dump_frame_loader(false);
+ qt_dump_resource_load_callbacks(false);
QWebFrame *mainFrame = m_page->mainFrame();
diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h
index 8d80f87..8309492 100644
--- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h
+++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h
@@ -53,6 +53,7 @@ QT_END_NAMESPACE
class QWebFrame;
class LayoutTestController;
+class DumpRenderTreeSupportQt;
class EventSender;
class TextInputController;
class GCController;
@@ -118,6 +119,7 @@ Q_SIGNALS:
private Q_SLOTS:
void showPage();
void hidePage();
+ void dryRunPrint(QWebFrame*);
private:
QString dumpFramesAsText(QWebFrame* frame);
diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp
index 1ef2d3f..7432052 100644
--- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp
+++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp
@@ -67,6 +67,8 @@ EventSender::EventSender(QWebPage* parent)
endOfQueue = 0;
startOfQueue = 0;
m_eventLoop = 0;
+ m_currentButton = 0;
+ resetClickCount();
m_page->view()->installEventFilter(this);
}
@@ -92,11 +94,28 @@ void EventSender::mouseDown(int button)
break;
}
+ // only consider a click to count, an event originated by the
+ // same previous button and at the same position.
+ if (m_currentButton == button
+ && m_mousePos == m_clickPos
+ && m_clickTimer.isActive())
+ m_clickCount++;
+ else
+ m_clickCount = 1;
+
+ m_currentButton = button;
+ m_clickPos = m_mousePos;
m_mouseButtons |= mouseButton;
// qDebug() << "EventSender::mouseDown" << frame;
- QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
+ QMouseEvent* event;
+ event = new QMouseEvent((m_clickCount == 2) ? QEvent::MouseButtonDblClick :
+ QEvent::MouseButtonPress, m_mousePos, m_mousePos,
+ mouseButton, m_mouseButtons, Qt::NoModifier);
+
sendOrQueueEvent(event);
+
+ m_clickTimer.start(QApplication::doubleClickInterval(), this);
}
void EventSender::mouseUp(int button)
@@ -289,11 +308,15 @@ void EventSender::scheduleAsynchronousClick()
void EventSender::addTouchPoint(int x, int y)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
- int id = m_touchPoints.count();
+ // Use index to refer to the position in the vector that this touch
+ // is stored. We then create a unique id for the touch that will be
+ // passed into WebCore.
+ int index = m_touchPoints.count();
+ int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1;
QTouchEvent::TouchPoint point(id);
m_touchPoints.append(point);
- updateTouchPoint(id, x, y);
- m_touchPoints[id].setState(Qt::TouchPointPressed);
+ updateTouchPoint(index, x, y);
+ m_touchPoints[index].setState(Qt::TouchPointPressed);
#endif
}
@@ -510,3 +533,8 @@ bool EventSender::eventFilter(QObject* watched, QEvent* event)
}
return false;
}
+
+void EventSender::timerEvent(QTimerEvent* ev)
+{
+ m_clickTimer.stop();
+}
diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h
index 38bca89..e824e0f 100644
--- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h
+++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h
@@ -30,6 +30,7 @@
#define EventSenderQt_h
#include <QApplication>
+#include <QBasicTimer>
#include <QEvent>
#include <QEventLoop>
#include <QMouseEvent>
@@ -50,6 +51,7 @@ class EventSender : public QObject {
public:
EventSender(QWebPage* parent);
virtual bool eventFilter(QObject* watched, QEvent* event);
+ void resetClickCount() { m_clickCount = 0; }
public slots:
void mouseDown(int button = 0);
@@ -73,18 +75,24 @@ public slots:
void clearTouchPoints();
void releaseTouchPoint(int index);
+protected:
+ void timerEvent(QTimerEvent*);
+
private:
void sendTouchEvent(QEvent::Type);
void sendOrQueueEvent(QEvent*);
void replaySavedEvents(bool flush);
QPoint m_mousePos;
+ QPoint m_clickPos;
Qt::MouseButtons m_mouseButtons;
QWebPage* m_page;
- int m_timeLeap;
+ int m_clickCount;
+ int m_currentButton;
bool m_mouseButtonPressed;
bool m_drag;
QEventLoop* m_eventLoop;
QWebFrame* frameUnderMouse() const;
+ QBasicTimer m_clickTimer;
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
QList<QTouchEvent::TouchPoint> m_touchPoints;
Qt::KeyboardModifiers m_touchModifiers;
diff --git a/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp
index 9cc3aa7..ba7e2c3 100644
--- a/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp
+++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp
@@ -29,14 +29,10 @@
#include "config.h"
#include "GCControllerQt.h"
+#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h"
#include <qwebpage.h>
-extern int qt_drt_javaScriptObjectsCount();
-extern void qt_drt_garbageCollector_collect();
-
-extern void qt_drt_garbageCollector_collectOnAlternateThread(bool waitUntilDone);
-
GCController::GCController(QWebPage* parent)
: QObject(parent)
{
@@ -44,15 +40,15 @@ GCController::GCController(QWebPage* parent)
void GCController::collect() const
{
- qt_drt_garbageCollector_collect();
+ DumpRenderTreeSupportQt::garbageCollectorCollect();
}
void GCController::collectOnAlternateThread(bool waitUntilDone) const
{
- qt_drt_garbageCollector_collectOnAlternateThread(waitUntilDone);
+ DumpRenderTreeSupportQt::garbageCollectorCollectOnAlternateThread(waitUntilDone);
}
size_t GCController::getJSObjectCount() const
{
- return qt_drt_javaScriptObjectsCount();
+ return DumpRenderTreeSupportQt::javaScriptObjectsCount();
}
diff --git a/WebKitTools/DumpRenderTree/qt/GCControllerQt.h b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h
index 8f5b432..ed2a858 100644
--- a/WebKitTools/DumpRenderTree/qt/GCControllerQt.h
+++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h
@@ -32,6 +32,7 @@
#include <QObject>
class QWebPage;
+class DumpRenderTreeSupportQt;
class GCController : public QObject
{
@@ -43,6 +44,7 @@ public slots:
void collect() const;
void collectOnAlternateThread(bool waitUntilDone) const;
size_t getJSObjectCount() const;
+
};
#endif
diff --git a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro
index 636835a..74fabf8 100644
--- a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro
+++ b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro
@@ -1,9 +1,10 @@
TARGET = ImageDiff
CONFIG -= app_bundle
+isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../..
include(../../../WebKit.pri)
INCLUDEPATH += ../../../JavaScriptCore
-DESTDIR = ../../../bin
+DESTDIR = $$OUTPUT_DIR/bin
QT = core gui
diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp
index a26bc3d..b95fe23 100644
--- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp
+++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp
@@ -28,6 +28,7 @@
*/
#include "config.h"
#include "LayoutTestControllerQt.h"
+#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h"
#include "DumpRenderTreeQt.h"
#include "WorkQueue.h"
@@ -36,32 +37,23 @@
#include <QLocale>
#include <qwebsettings.h>
+extern void qt_wrt_setViewMode(QWebPage* page, const QString& mode);
extern void qt_dump_editing_callbacks(bool b);
extern void qt_dump_frame_loader(bool b);
extern void qt_dump_resource_load_callbacks(bool b);
-extern void qt_drt_setFrameSetFlatteningEnabled(QWebPage*, bool);
-extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled);
-extern void qt_drt_setTimelineProfilingEnabled(QWebPage*, 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 bool qt_drt_pauseSVGAnimation(QWebFrame*, const QString& animationId, double time, const QString& elementId);
-extern int qt_drt_numberOfActiveAnimations(QWebFrame*);
-extern void qt_drt_setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme);
-
-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);
-extern int qt_drt_workerThreadCount();
-extern int qt_drt_pageNumberForElementById(QWebFrame* qFrame, const QString& id, float width, float height);
-extern int qt_drt_numberOfPages(QWebFrame* qFrame, float width, float height);
-extern void qt_drt_webinspector_executeScript(QWebPage* page, long callId, const QString& script);
-extern void qt_drt_webinspector_show(QWebPage *page);
-extern void qt_drt_webinspector_close(QWebPage *page);
+extern void qt_set_will_send_request_returns_null_on_redirect(bool b);
+extern void qt_set_will_send_request_returns_null(bool b);
+extern void qt_set_will_send_request_clear_headers(const QStringList& headers);
+
+extern void qt_dump_notification(bool b);
LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt)
: QObject()
, m_drt(drt)
{
+ qRegisterMetaType<QWebElement>("QWebElement");
reset();
+ qt_dump_notification(true);
}
void LayoutTestController::reset()
@@ -85,6 +77,9 @@ void LayoutTestController::reset()
qt_dump_editing_callbacks(false);
qt_dump_frame_loader(false);
qt_dump_resource_load_callbacks(false);
+ qt_set_will_send_request_returns_null_on_redirect(false);
+ qt_set_will_send_request_returns_null(false);
+ qt_set_will_send_request_clear_headers(QStringList());
emit hidePage();
}
@@ -136,12 +131,17 @@ void LayoutTestController::waitUntilDone()
{
//qDebug() << ">>>>waitForDone";
m_waitForDone = true;
- m_timeoutTimer.start(15000, this);
+ m_timeoutTimer.start(30000, this);
}
QString LayoutTestController::counterValueForElementById(const QString& id)
{
- return qt_drt_counterValueForElementById(m_drt->webPage()->mainFrame(), id);
+ return DumpRenderTreeSupportQt::counterValueForElementById(m_drt->webPage()->mainFrame(), id);
+}
+
+void LayoutTestController::setViewModeMediaFeature(const QString& mode)
+{
+ qt_wrt_setViewMode(m_drt->webPage(), mode);
}
int LayoutTestController::webHistoryItemCount()
@@ -188,6 +188,17 @@ int LayoutTestController::windowCount()
return m_drt->windowCount();
}
+void LayoutTestController::grantDesktopNotificationPermission(const QString& origin)
+{
+ // FIXME: Implement for notification security
+}
+
+bool LayoutTestController::checkDesktopNotificationPermission(const QString& origin)
+{
+ // FIXME: Implement for notification security
+ return true;
+}
+
void LayoutTestController::display()
{
emit showPage();
@@ -220,6 +231,21 @@ void LayoutTestController::dumpResourceLoadCallbacks()
qt_dump_resource_load_callbacks(true);
}
+void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(bool enabled)
+{
+ qt_set_will_send_request_returns_null_on_redirect(enabled);
+}
+
+void LayoutTestController::setWillSendRequestReturnsNull(bool enabled)
+{
+ qt_set_will_send_request_returns_null(enabled);
+}
+
+void LayoutTestController::setWillSendRequestClearHeader(const QStringList& headers)
+{
+ qt_set_will_send_request_clear_headers(headers);
+}
+
void LayoutTestController::queueBackNavigation(int howFarBackward)
{
//qDebug() << ">>>queueBackNavigation" << howFarBackward;
@@ -290,27 +316,36 @@ QString LayoutTestController::decodeHostName(const QString& host)
return decoded;
}
+void LayoutTestController::setMediaType(const QString& type)
+{
+ DumpRenderTreeSupportQt::setMediaType(m_drt->webPage()->mainFrame(), type);
+}
void LayoutTestController::closeWebInspector()
{
- qt_drt_webinspector_close(m_drt->webPage());
+ DumpRenderTreeSupportQt::webInspectorClose(m_drt->webPage());
m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, false);
}
+void LayoutTestController::setDeveloperExtrasEnabled(bool enabled)
+{
+ m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enabled);
+}
+
void LayoutTestController::showWebInspector()
{
m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
- qt_drt_webinspector_show(m_drt->webPage());
+ DumpRenderTreeSupportQt::webInspectorShow(m_drt->webPage());
}
void LayoutTestController::evaluateInWebInspector(long callId, const QString& script)
{
- qt_drt_webinspector_executeScript(m_drt->webPage(), callId, script);
+ DumpRenderTreeSupportQt::webInspectorExecuteScript(m_drt->webPage(), callId, script);
}
-void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
+void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
{
- qt_drt_setFrameSetFlatteningEnabled(m_drt->webPage(), enabled);
+ DumpRenderTreeSupportQt::setFrameFlatteningEnabled(m_drt->webPage(), enabled);
}
void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
@@ -323,15 +358,20 @@ void LayoutTestController::setAllowFileAccessFromFileURLs(bool enabled)
m_drt->webPage()->settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, enabled);
}
+void LayoutTestController::setAppCacheMaximumSize(unsigned long long quota)
+{
+ m_drt->webPage()->settings()->setOfflineWebApplicationCacheQuota(quota);
+}
+
void LayoutTestController::setJavaScriptProfilingEnabled(bool enable)
{
- m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
- qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable);
+ setDeveloperExtrasEnabled(enable);
+ DumpRenderTreeSupportQt::setJavaScriptProfilingEnabled(m_topLoadingFrame, enable);
}
void LayoutTestController::setTimelineProfilingEnabled(bool enable)
{
- qt_drt_setTimelineProfilingEnabled(m_drt->webPage(), enable);
+ DumpRenderTreeSupportQt::setTimelineProfilingEnabled(m_drt->webPage(), enable);
}
void LayoutTestController::setFixedContentsSize(int width, int height)
@@ -344,6 +384,11 @@ void LayoutTestController::setPrivateBrowsingEnabled(bool enable)
m_drt->webPage()->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable);
}
+void LayoutTestController::setSpatialNavigationEnabled(bool enable)
+{
+ m_drt->webPage()->settings()->setAttribute(QWebSettings::SpatialNavigationEnabled, enable);
+}
+
void LayoutTestController::setPopupBlockingEnabled(bool enable)
{
m_drt->webPage()->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, !enable);
@@ -367,12 +412,12 @@ void LayoutTestController::setMainFrameIsFirstResponder(bool isFirst)
void LayoutTestController::setXSSAuditorEnabled(bool enable)
{
- // Set XSSAuditorEnabled globally so that windows created by the test inherit it too.
+ // Set XSSAuditingEnabled globally so that windows created by the test inherit it too.
// resetSettings() will call this to reset the page and global setting to false again.
// Needed by http/tests/security/xssAuditor/link-opens-new-window.html
QWebSettings* globalSettings = QWebSettings::globalSettings();
- globalSettings->setAttribute(QWebSettings::XSSAuditorEnabled, enable);
- m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditorEnabled, enable);
+ globalSettings->setAttribute(QWebSettings::XSSAuditingEnabled, enable);
+ m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditingEnabled, enable);
}
bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& animationName,
@@ -381,7 +426,7 @@ bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& an
{
QWebFrame* frame = m_drt->webPage()->mainFrame();
Q_ASSERT(frame);
- return qt_drt_pauseAnimation(frame, animationName, time, elementId);
+ return DumpRenderTreeSupportQt::pauseAnimation(frame, animationName, time, elementId);
}
bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& propertyName,
@@ -390,7 +435,7 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& p
{
QWebFrame* frame = m_drt->webPage()->mainFrame();
Q_ASSERT(frame);
- return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId);
+ return DumpRenderTreeSupportQt::pauseTransitionOfProperty(frame, propertyName, time, elementId);
}
bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& animationId,
@@ -399,14 +444,14 @@ bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& ani
{
QWebFrame* frame = m_drt->webPage()->mainFrame();
Q_ASSERT(frame);
- return qt_drt_pauseSVGAnimation(frame, animationId, time, elementId);
+ return DumpRenderTreeSupportQt::pauseSVGAnimation(frame, animationId, time, elementId);
}
unsigned LayoutTestController::numberOfActiveAnimations() const
{
QWebFrame* frame = m_drt->webPage()->mainFrame();
Q_ASSERT(frame);
- return qt_drt_numberOfActiveAnimations(frame);
+ return DumpRenderTreeSupportQt::numberOfActiveAnimations(frame);
}
void LayoutTestController::disableImageLoading()
@@ -432,9 +477,14 @@ void LayoutTestController::clearAllDatabases()
QWebDatabase::removeAllDatabases();
}
-void LayoutTestController::whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains)
+void LayoutTestController::addOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains)
+{
+ DumpRenderTreeSupportQt::whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains);
+}
+
+void LayoutTestController::removeOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains)
{
- qt_drt_whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains);
+ // FIXME: Implement.
}
void LayoutTestController::waitForPolicyDelegate()
@@ -457,6 +507,10 @@ void LayoutTestController::overridePreference(const QString& name, const QVarian
settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt());
else if (name == "WebKitUsesPageCachePreferenceKey")
QWebSettings::setMaximumPagesInCache(value.toInt());
+ else if (name == "WebKitEnableCaretBrowsing")
+ setCaretBrowsingEnabled(value.toBool());
+ else if (name == "WebKitPluginsEnabled")
+ settings->setAttribute(QWebSettings::PluginsEnabled, value.toBool());
else
printf("ERROR: LayoutTestController::overridePreference() does not support the '%s' preference\n",
name.toLatin1().data());
@@ -467,6 +521,11 @@ void LayoutTestController::setUserStyleSheetLocation(const QString& url)
m_userStyleSheetLocation = QUrl(url);
}
+void LayoutTestController::setCaretBrowsingEnabled(bool value)
+{
+ DumpRenderTreeSupportQt::setCaretBrowsingEnabled(m_drt->webPage(), value);
+}
+
void LayoutTestController::setUserStyleSheetEnabled(bool enabled)
{
if (enabled)
@@ -477,12 +536,12 @@ void LayoutTestController::setUserStyleSheetEnabled(bool enabled)
void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme)
{
- qt_drt_setDomainRelaxationForbiddenForURLScheme(forbidden, scheme);
+ DumpRenderTreeSupportQt::setDomainRelaxationForbiddenForURLScheme(forbidden, scheme);
}
int LayoutTestController::workerThreadCount()
{
- return qt_drt_workerThreadCount();
+ return DumpRenderTreeSupportQt::workerThreadCount();
}
int LayoutTestController::pageNumberForElementById(const QString& id, float width, float height)
@@ -493,10 +552,74 @@ int LayoutTestController::pageNumberForElementById(const QString& id, float widt
height = m_drt->webPage()->viewportSize().height();
}
- return qt_drt_pageNumberForElementById(m_drt->webPage()->mainFrame(), id, width, height);
+ return DumpRenderTreeSupportQt::pageNumberForElementById(m_drt->webPage()->mainFrame(), id, width, height);
}
int LayoutTestController::numberOfPages(float width, float height)
{
- return qt_drt_numberOfPages(m_drt->webPage()->mainFrame(), width, height);
+ return DumpRenderTreeSupportQt::numberOfPages(m_drt->webPage()->mainFrame(), width, height);
+}
+
+bool LayoutTestController::callShouldCloseOnWebView()
+{
+ // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27481
+ return false;
+}
+
+void LayoutTestController::setScrollbarPolicy(const QString& orientation, const QString& policy)
+{
+ Qt::Orientation o;
+ Qt::ScrollBarPolicy p;
+
+ if (orientation == "vertical")
+ o = Qt::Vertical;
+ else if (orientation == "horizontal")
+ o = Qt::Horizontal;
+ else
+ return;
+
+ if (policy == "on")
+ p = Qt::ScrollBarAlwaysOn;
+ else if (policy == "auto")
+ p = Qt::ScrollBarAsNeeded;
+ else if (policy == "off")
+ p = Qt::ScrollBarAlwaysOff;
+ else
+ return;
+
+ m_drt->webPage()->mainFrame()->setScrollBarPolicy(o, p);
+}
+
+void LayoutTestController::setSmartInsertDeleteEnabled(bool enable)
+{
+ DumpRenderTreeSupportQt::setSmartInsertDeleteEnabled(m_drt->webPage(), enable);
}
+
+void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool enable)
+{
+ DumpRenderTreeSupportQt::setSelectTrailingWhitespaceEnabled(m_drt->webPage(), enable);
+}
+
+void LayoutTestController::execCommand(const QString& name, const QString& value)
+{
+ DumpRenderTreeSupportQt::executeCoreCommandByName(m_drt->webPage(), name, value);
+}
+
+bool LayoutTestController::isCommandEnabled(const QString& name) const
+{
+ return DumpRenderTreeSupportQt::isCommandEnabled(m_drt->webPage(), name);
+}
+
+QString LayoutTestController::markerTextForListItem(const QWebElement& listItem)
+{
+ return DumpRenderTreeSupportQt::markerTextForListItem(listItem);
+}
+
+void LayoutTestController::authenticateSession(const QString&, const QString&, const QString&)
+{
+ // FIXME: If there is a concept per-session (per-process) credential storage, the credentials should be added to it for later use.
+}
+
+
+const unsigned LayoutTestController::maxViewWidth = 800;
+const unsigned LayoutTestController::maxViewHeight = 600;
diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h
index d73794e..df645e1 100644
--- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h
+++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h
@@ -40,12 +40,14 @@
#include <QVariant>
#include <qwebdatabase.h>
+#include <qwebelement.h>
#include <qwebframe.h>
#include <qwebhistory.h>
#include <qwebpage.h>
#include <qwebsecurityorigin.h>
class QWebFrame;
+class DumpRenderTreeSupportQt;
namespace WebCore {
class DumpRenderTree;
}
@@ -70,6 +72,9 @@ public:
void reset();
+ static const unsigned int maxViewWidth;
+ static const unsigned int maxViewHeight;
+
protected:
void timerEvent(QTimerEvent*);
@@ -98,6 +103,9 @@ public slots:
void dumpEditingCallbacks();
void dumpFrameLoadCallbacks();
void dumpResourceLoadCallbacks();
+ void setWillSendRequestReturnsNullOnRedirect(bool enabled);
+ void setWillSendRequestReturnsNull(bool enabled);
+ void setWillSendRequestClearHeader(const QStringList& headers);
void queueBackNavigation(int howFarBackward);
void queueForwardNavigation(int howFarForward);
void queueLoad(const QString& url, const QString& target = QString());
@@ -107,6 +115,8 @@ public slots:
void provisionalLoad();
void setCloseRemainingWindowsWhenComplete(bool = false) {}
int windowCount();
+ void grantDesktopNotificationPermission(const QString& origin);
+ bool checkDesktopNotificationPermission(const QString& origin);
void display();
void clearBackForwardList();
QString pathToLocalResource(const QString& url);
@@ -114,23 +124,33 @@ public slots:
QString encodeHostName(const QString& host);
QString decodeHostName(const QString& host);
void dumpSelectionRect() const {}
+ void setDeveloperExtrasEnabled(bool);
void showWebInspector();
void closeWebInspector();
void evaluateInWebInspector(long callId, const QString& script);
- void setFrameSetFlatteningEnabled(bool enable);
+ void setMediaType(const QString& type);
+ void setFrameFlatteningEnabled(bool enable);
void setAllowUniversalAccessFromFileURLs(bool enable);
void setAllowFileAccessFromFileURLs(bool enable);
+ void setAppCacheMaximumSize(unsigned long long quota);
void setJavaScriptProfilingEnabled(bool enable);
void setTimelineProfilingEnabled(bool enable);
void setFixedContentsSize(int width, int height);
void setPrivateBrowsingEnabled(bool enable);
+ void setSpatialNavigationEnabled(bool enabled);
void setPopupBlockingEnabled(bool enable);
void setPOSIXLocale(const QString& locale);
void resetLoadFinished() { m_loadFinished = false; }
void setWindowIsKey(bool isKey);
void setMainFrameIsFirstResponder(bool isFirst);
void setXSSAuditorEnabled(bool enable);
+ void setCaretBrowsingEnabled(bool enable);
+ void setViewModeMediaFeature(const QString& mode);
+ void setSmartInsertDeleteEnabled(bool enable);
+ void setSelectTrailingWhitespaceEnabled(bool enable);
+ void execCommand(const QString& name, const QString& value = QString());
+ bool isCommandEnabled(const QString& name) const;
bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId);
bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId);
@@ -138,7 +158,8 @@ public slots:
unsigned numberOfActiveAnimations() const;
- void whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains);
+ void addOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains);
+ void removeOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains);
void dispatchPendingLoadRequests();
void disableImageLoading();
@@ -153,7 +174,19 @@ public slots:
void setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme);
int workerThreadCount();
int pageNumberForElementById(const QString& id, float width = 0, float height = 0);
- int numberOfPages(float width, float height);
+ int numberOfPages(float width = maxViewWidth, float height = maxViewHeight);
+ bool callShouldCloseOnWebView();
+
+ /*
+ Policy values: 'on', 'auto' or 'off'.
+ Orientation values: 'vertical' or 'horizontal'.
+ */
+ void setScrollbarPolicy(const QString& orientation, const QString& policy);
+
+ QString markerTextForListItem(const QWebElement& listItem);
+
+ // Simulate a request an embedding application could make, populating per-session credential storage.
+ void authenticateSession(const QString& url, const QString& username, const QString& password);
private slots:
void processWork();
diff --git a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro
index 7b8162b..9b19231 100644
--- a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro
+++ b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro
@@ -2,6 +2,7 @@ TEMPLATE = lib
TARGET = TestNetscapePlugIn
VPATH = ../../unix/TestNetscapePlugin ../../TestNetscapePlugIn.subproj
+isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../..
include(../../../../WebKit.pri)
DESTDIR = $$OUTPUT_DIR/lib/plugins
diff --git a/WebKitTools/DumpRenderTree/qt/main.cpp b/WebKitTools/DumpRenderTree/qt/main.cpp
index 7d72982..7d1c08c 100644
--- a/WebKitTools/DumpRenderTree/qt/main.cpp
+++ b/WebKitTools/DumpRenderTree/qt/main.cpp
@@ -93,7 +93,7 @@ QString get_backtrace() {
return s;
}
-#ifndef Q_OS_WIN
+#if HAVE(SIGNAL_H)
static NO_RETURN void crashHandler(int sig)
{
fprintf(stderr, "%s\n", strsignal(sig));
@@ -114,10 +114,7 @@ int main(int argc, char* argv[])
WebCore::DumpRenderTree::initializeFonts();
#endif
-#if QT_VERSION >= 0x040500
QApplication::setGraphicsSystem("raster");
-#endif
-
QApplication::setStyle(new QWindowsStyle);
QFont f("Sans Serif");
@@ -132,7 +129,7 @@ int main(int argc, char* argv[])
QX11Info::setAppDpiX(0, 96);
#endif
-#ifndef Q_OS_WIN
+#if HAVE(SIGNAL_H)
signal(SIGILL, crashHandler); /* 4: illegal instruction (not reset when caught) */
signal(SIGTRAP, crashHandler); /* 5: trace trap (not reset when caught) */
signal(SIGFPE, crashHandler); /* 8: floating point exception */
diff --git a/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp
index cb01267..a3c6773 100644
--- a/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp
+++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp
@@ -58,6 +58,7 @@ webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/,
{
if (browser->version >= 14) {
PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass());
+ instance->pdata = obj;
for (int i = 0; i < argc; i++) {
if (strcasecmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad)
@@ -85,8 +86,11 @@ webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/,
obj->testDocumentOpenInDestroyStream = TRUE;
else if (strcasecmp(argn[i], "testwindowopen") == 0)
obj->testWindowOpen = TRUE;
+ else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow)
+ obj->onSetWindow = strdup(argv[i]);
}
- instance->pdata = obj;
+
+ browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode);
}
return NPERR_NO_ERROR;
@@ -114,6 +118,9 @@ webkit_test_plugin_destroy_instance(NPP instance, NPSavedData** /*save*/)
if (obj->logDestroy)
pluginLog(instance, "NPP_Destroy");
+ if (obj->onSetWindow)
+ free(obj->onSetWindow);
+
browser->releaseobject(&obj->header);
}
@@ -126,10 +133,14 @@ webkit_test_plugin_set_window(NPP instance, NPWindow *window)
PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
if (obj) {
+ obj->lastWindow = *window;
+
if (obj->logSetWindow) {
pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height);
obj->logSetWindow = false;
}
+ if (obj->onSetWindow)
+ executeScript(obj, obj->onSetWindow);
if (obj->testWindowOpen) {
testWindowOpen(instance);
@@ -282,9 +293,17 @@ webkit_test_plugin_get_value(NPP instance, NPPVariable variable, void *value)
}
static NPError
-webkit_test_plugin_set_value(NPP /*instance*/, NPNVariable /*variable*/, void* /*value*/)
+webkit_test_plugin_set_value(NPP instance, NPNVariable variable, void* value)
{
- return NPERR_NO_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;
+ }
}
char *
diff --git a/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp b/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp
index 6b35948..255bfc3 100644
--- a/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp
+++ b/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp
@@ -59,6 +59,12 @@ AccessibilityController::~AccessibilityController()
JSValueUnprotect(frame->globalContext(), it->second);
}
+AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y)
+{
+ // FIXME: implement
+ return 0;
+}
+
AccessibilityUIElement AccessibilityController::focusedElement()
{
COMPtr<IAccessible> rootAccessible = rootElement().platformUIElement();
diff --git a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp
index 301112f..9f00ae4 100644
--- a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp
+++ b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp
@@ -83,6 +83,18 @@ int AccessibilityUIElement::childrenCount()
return childCount;
}
+int AccessibilityUIElement::rowCount()
+{
+ // FIXME: implement
+ return 0;
+}
+
+int AccessibilityUIElement::columnCount()
+{
+ // FIXME: implement
+ return 0;
+}
+
AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
{
return 0;
@@ -224,6 +236,11 @@ JSStringRef AccessibilityUIElement::language()
return JSStringCreateWithCharacters(0, 0);
}
+JSStringRef AccessibilityUIElement::helpText() const
+{
+ return 0;
+}
+
double AccessibilityUIElement::x()
{
long x, y, width, height;
@@ -477,6 +494,11 @@ void AccessibilityUIElement::showMenu()
m_element->accDoDefaultAction(self());
}
+void AccessibilityUIElement::press()
+{
+ // FIXME: implement
+}
+
AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
{
return 0;
@@ -540,6 +562,11 @@ bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallbac
return true;
}
+void AccessibilityUIElement::removeNotificationListener()
+{
+ // FIXME: implement
+}
+
bool AccessibilityUIElement::isSelectable() const
{
DWORD state = accessibilityState(m_element);
diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp
index ddfca95..f9b40d1 100644
--- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp
+++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp
@@ -84,6 +84,7 @@ static bool printSeparators;
static bool leakChecking = false;
static bool threaded = false;
static bool forceComplexText = false;
+static bool printSupportedFeatures = false;
static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
volatile bool done;
@@ -731,6 +732,11 @@ static bool shouldOpenWebInspector(const char* pathOrURL)
return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\");
}
+static bool shouldEnableDeveloperExtras(const char* pathOrURL)
+{
+ return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "/inspector-enabled/") || strstr(pathOrURL, "\\inspector-enabled\\");
+}
+
static void resetDefaultsToConsistentValues(IWebPreferences* preferences)
{
#ifdef USE_MAC_FONTS
@@ -790,7 +796,7 @@ static void resetDefaultsToConsistentValues(IWebPreferences* preferences)
prefsPrivate->setExperimentalNotificationsEnabled(TRUE);
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->setFrameSetFlatteningEnabled(FALSE);
+ prefsPrivate->setFrameFlatteningEnabled(FALSE);
prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE);
}
setAlwaysAcceptCookies(false);
@@ -836,7 +842,7 @@ static void resetWebViewToConsistentStateBeforeTesting()
SetFocus(viewWindow);
webViewPrivate->clearMainFrameName();
- webViewPrivate->resetOriginAccessWhiteLists();
+ webViewPrivate->resetOriginAccessWhitelists();
BSTR groupName;
if (SUCCEEDED(webView->groupName(&groupName))) {
@@ -910,8 +916,11 @@ static void runTest(const string& testPathOrURL)
resetWebViewToConsistentStateBeforeTesting();
- if (shouldOpenWebInspector(pathOrURL.c_str()))
- gLayoutTestController->showWebInspector();
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str())) {
+ gLayoutTestController->setDeveloperExtrasEnabled(true);
+ if (shouldOpenWebInspector(pathOrURL.c_str()))
+ gLayoutTestController->showWebInspector();
+ }
prevTestBFItem = 0;
if (webView) {
@@ -947,7 +956,7 @@ static void runTest(const string& testPathOrURL)
DispatchMessage(&msg);
}
- if (shouldOpenWebInspector(pathOrURL.c_str()))
+ if (shouldEnableDeveloperExtras(pathOrURL.c_str()))
gLayoutTestController->closeWebInspector();
resetWebViewToConsistentStateBeforeTesting();
@@ -1222,6 +1231,11 @@ int main(int argc, char* argv[])
continue;
}
+ if (!stricmp(argv[i], "--print-supported-features")) {
+ printSupportedFeatures = true;
+ continue;
+ }
+
tests.append(argv[i]);
}
@@ -1246,6 +1260,20 @@ int main(int argc, char* argv[])
standardPreferences->setJavaScriptEnabled(TRUE);
standardPreferences->setDefaultFontSize(16);
+ if (printSupportedFeatures) {
+ BOOL acceleratedCompositingAvailable;
+ standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable);
+ BOOL threeDRenderingAvailable =
+#if ENABLE(3D_RENDERING)
+ true;
+#else
+ false;
+#endif
+
+ printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : "");
+ return 0;
+ }
+
COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow));
if (!webView)
return -1;
diff --git a/WebKitTools/DumpRenderTree/win/EventSender.cpp b/WebKitTools/DumpRenderTree/win/EventSender.cpp
index 5a42b00..2a36d8d 100644
--- a/WebKitTools/DumpRenderTree/win/EventSender.cpp
+++ b/WebKitTools/DumpRenderTree/win/EventSender.cpp
@@ -667,20 +667,21 @@ static JSClassRef getClass(JSContextRef context)
return eventSenderClass;
}
-JSObjectRef makeEventSender(JSContextRef context)
+JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
{
- down = false;
- dragMode = true;
- replayingSavedEvents = false;
- timeOffset = 0;
- lastMousePosition.x = 0;
- lastMousePosition.y = 0;
-
- endOfQueue = 0;
- startOfQueue = 0;
+ if (isTopFrame) {
+ down = false;
+ dragMode = true;
+ replayingSavedEvents = false;
+ timeOffset = 0;
+ lastMousePosition.x = 0;
+ lastMousePosition.y = 0;
- didDragEnter = false;
- draggingInfo = 0;
+ endOfQueue = 0;
+ startOfQueue = 0;
+ didDragEnter = false;
+ draggingInfo = 0;
+ }
return JSObjectMake(context, getClass(context), 0);
}
diff --git a/WebKitTools/DumpRenderTree/win/EventSender.h b/WebKitTools/DumpRenderTree/win/EventSender.h
index 79d7dab..a0add85 100644
--- a/WebKitTools/DumpRenderTree/win/EventSender.h
+++ b/WebKitTools/DumpRenderTree/win/EventSender.h
@@ -35,7 +35,7 @@ typedef long HRESULT;
typedef const struct OpaqueJSContext* JSContextRef;
typedef struct OpaqueJSValue* JSObjectRef;
-JSObjectRef makeEventSender(JSContextRef context);
+JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame);
void replaySavedEvents(HRESULT* oleDragAndDropReturnValue = 0);
extern DraggingInfo* draggingInfo;
diff --git a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp
index 37d5e1c..29f99ab 100644
--- a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp
+++ b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp
@@ -336,6 +336,9 @@ void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* f
JSGlobalContextRef context = frame->globalContext();
JSObjectRef windowObject = JSContextGetGlobalObject(context);
+ IWebFrame* parentFrame = 0;
+ frame->parentFrame(&parentFrame);
+
JSValueRef exception = 0;
::gLayoutTestController->makeWindowObject(context, windowObject, &exception);
@@ -348,7 +351,7 @@ void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* f
ASSERT(!exception);
JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender");
- JSValueRef eventSender = makeEventSender(context);
+ JSValueRef eventSender = makeEventSender(context, !parentFrame);
JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
JSStringRelease(eventSenderStr);
}
diff --git a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
index 9f84488..24ddc3b 100644
--- a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
+++ b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -110,6 +110,21 @@ void LayoutTestController::clearBackForwardList()
backForwardList->goToItem(item.get());
}
+bool LayoutTestController::callShouldCloseOnWebView()
+{
+ COMPtr<IWebView> webView;
+ if (FAILED(frame->webView(&webView)))
+ return false;
+
+ COMPtr<IWebViewPrivate> viewPrivate;
+ if (FAILED(webView->QueryInterface(&viewPrivate)))
+ return false;
+
+ BOOL result;
+ viewPrivate->shouldClose(&result);
+ return result;
+}
+
JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name)
{
// FIXME: Implement!
@@ -158,6 +173,33 @@ void LayoutTestController::keepWebHistory()
history->setOptionalSharedHistory(sharedHistory.get());
}
+JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value)
+{
+ // FIXME: Implement this.
+ return JSValueMakeUndefined(context);
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
+{
+ COMPtr<IWebFramePrivate> framePrivate(Query, frame);
+ if (!framePrivate)
+ return false;
+
+ BSTR textBSTR = 0;
+ HRESULT hr = framePrivate->layerTreeAsText(&textBSTR);
+
+ wstring text(textBSTR, SysStringLen(textBSTR));
+ SysFreeString(textBSTR);
+ JSRetainPtr<JSStringRef> textValueJS(Adopt, JSStringCreateWithCharacters(text.data(), text.length()));
+ return textValueJS;
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const
+{
+ // FIXME: Implement me.
+ return JSRetainPtr<JSStringRef>();
+}
+
void LayoutTestController::waitForPolicyDelegate()
{
// FIXME: Implement this.
@@ -372,7 +414,7 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled)
prefsPrivate->setXSSAuditorEnabled(enabled);
}
-void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
+void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
{
COMPtr<IWebView> webView;
if (FAILED(frame->webView(&webView)))
@@ -386,7 +428,12 @@ void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
if (!prefsPrivate)
return;
- prefsPrivate->setFrameSetFlatteningEnabled(enabled);
+ prefsPrivate->setFrameFlatteningEnabled(enabled);
+}
+
+void LayoutTestController::setSpatialNavigationEnabled(bool enabled)
+{
+ // FIXME: Implement for SpatialNavigation layout tests.
}
void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
@@ -688,19 +735,11 @@ void LayoutTestController::setJavaScriptProfilingEnabled(bool flag)
if (FAILED(webView->QueryInterface(&viewPrivate)))
return;
- COMPtr<IWebPreferences> preferences;
- if (FAILED(webView->preferences(&preferences)))
- return;
-
- COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
- if (!prefsPrivate)
- return;
-
COMPtr<IWebInspector> inspector;
if (FAILED(viewPrivate->inspector(&inspector)))
return;
- prefsPrivate->setDeveloperExtrasEnabled(flag);
+ setDeveloperExtrasEnabled(flag);
inspector->setJavaScriptProfilingEnabled(flag);
}
@@ -717,7 +756,7 @@ void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag)
viewEditing->setSelectTrailingWhitespaceEnabled(flag ? TRUE : FALSE);
}
-static const CFTimeInterval waitToDumpWatchdogInterval = 15.0;
+static const CFTimeInterval waitToDumpWatchdogInterval = 30.0;
static void CALLBACK waitUntilDoneWatchdogFired(HWND, UINT, UINT_PTR, DWORD)
{
@@ -950,13 +989,27 @@ static _bstr_t bstrT(JSStringRef jsString)
return _bstr_t(JSStringCopyBSTR(jsString), false);
}
-void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
{
COMPtr<IWebViewPrivate> webView;
if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView))))
return;
- webView->whiteListAccessFromOrigin(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains);
+ webView->addOriginAccessWhitelistEntry(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains);
+}
+
+void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+{
+ COMPtr<IWebViewPrivate> webView;
+ if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView))))
+ return;
+
+ webView->removeOriginAccessWhitelistEntry(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains);
+}
+
+void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
+{
+ // FIXME: implement
}
void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
@@ -986,7 +1039,7 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source)
webView->addUserStyleSheetToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), world.get(), bstrT(source).GetBSTR(), 0, 0, 0, 0, 0);
}
-void LayoutTestController::showWebInspector()
+void LayoutTestController::setDeveloperExtrasEnabled(bool enabled)
{
COMPtr<IWebView> webView;
if (FAILED(frame->webView(&webView)))
@@ -1000,7 +1053,14 @@ void LayoutTestController::showWebInspector()
if (!prefsPrivate)
return;
- prefsPrivate->setDeveloperExtrasEnabled(true);
+ prefsPrivate->setDeveloperExtrasEnabled(enabled);
+}
+
+void LayoutTestController::showWebInspector()
+{
+ COMPtr<IWebView> webView;
+ if (FAILED(frame->webView(&webView)))
+ return;
COMPtr<IWebViewPrivate> viewPrivate(Query, webView);
if (!viewPrivate)
@@ -1026,16 +1086,6 @@ void LayoutTestController::closeWebInspector()
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)
@@ -1169,3 +1219,29 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data,
{
}
+
+void LayoutTestController::apiTestGoToCurrentBackForwardItem()
+{
+ COMPtr<IWebView> webView;
+ if (FAILED(frame->webView(&webView)))
+ return;
+
+ COMPtr<IWebBackForwardList> backForwardList;
+ if (FAILED(webView->backForwardList(&backForwardList)))
+ return;
+
+ COMPtr<IWebHistoryItem> item;
+ if (FAILED(backForwardList->currentItem(&item)))
+ return;
+
+ BOOL success;
+ webView->goToBackForwardItem(item.get(), &success);
+}
+
+void LayoutTestController::setWebViewEditable(bool)
+{
+}
+
+void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef)
+{
+}
diff --git a/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp
index 19bf84a..2e031da 100644
--- a/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp
+++ b/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp
@@ -31,28 +31,35 @@
#include "DumpRenderTree.h"
#include "LayoutTestController.h"
-#include <comutil.h>
#include <WebKit/WebKitCOMAPI.h>
+#include <comutil.h>
+#include <sstream>
+#include <tchar.h>
#include <wtf/HashMap.h>
#include <wtf/Vector.h>
-#include <sstream>
-
-using std::wstring;
-using std::wiostream;
+using namespace std;
static inline wstring wstringFromBSTR(BSTR str)
{
return wstring(str, ::SysStringLen(str));
}
-wstring wstringFromInt(int i)
+static inline wstring wstringFromInt(int i)
{
- std::wostringstream ss;
+ wostringstream ss;
ss << i;
return ss.str();
}
+static inline BSTR BSTRFromString(const string& str)
+{
+ int length = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0);
+ BSTR result = ::SysAllocStringLen(0, length);
+ ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), result, length);
+ return result;
+}
+
typedef HashMap<unsigned long, wstring> IdentifierMap;
IdentifierMap& urlMap()
@@ -254,8 +261,16 @@ HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest(
return S_OK;
}
- request->AddRef();
- *newRequest = request;
+ IWebMutableURLRequest* requestCopy = 0;
+ request->mutableCopy(&requestCopy);
+ const set<string>& clearHeaders = gLayoutTestController->willSendRequestClearHeaders();
+ for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) {
+ BSTR bstrHeader = BSTRFromString(*header);
+ requestCopy->setValue(0, bstrHeader);
+ SysFreeString(bstrHeader);
+ }
+
+ *newRequest = requestCopy;
return S_OK;
}
diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp
index 08a2f6a..eeacb7e 100644
--- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp
+++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp
@@ -6,7 +6,7 @@
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in
this original Apple software (the "Apple Software"), to use, reproduce, modify and
redistribute the Apple Software, with or without modifications, in source and/or binary
forms; provided that if you redistribute the Apple Software in its entirety and without
@@ -89,7 +89,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch
{
if (browser->version >= 14) {
PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass());
-
+ instance->pdata = obj;
+
for (int16 i = 0; i < argc; i++) {
if (_stricmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad)
obj->onStreamLoad = _strdup(argv[i]);
@@ -107,9 +108,11 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch
obj->testDocumentOpenInDestroyStream = TRUE;
else if (_stricmp(argn[i], "testwindowopen") == 0)
obj->testWindowOpen = TRUE;
+ else if (_stricmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow)
+ obj->onSetWindow = strdup(argv[i]);
}
-
- instance->pdata = obj;
+
+ browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode);
}
return NPERR_NO_ERROR;
@@ -136,6 +139,9 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save)
if (obj->logDestroy)
printf("PLUGIN: NPP_Destroy\n");
+ if (obj->onSetWindow)
+ free(obj->onSetWindow);
+
browser->releaseobject(&obj->header);
}
return NPERR_NO_ERROR;
@@ -146,6 +152,11 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window)
PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
if (obj) {
+ obj->lastWindow = *window;
+
+ if (obj->onSetWindow)
+ executeScript(obj, obj->onSetWindow);
+
if (obj->testWindowOpen) {
testWindowOpen(instance);
obj->testWindowOpen = FALSE;
@@ -238,5 +249,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/wx/LayoutTestControllerWx.cpp b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp
index ce1bda5..873e8d8 100644
--- a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp
+++ b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp
@@ -35,6 +35,8 @@
#include <JavaScriptCore/JSRetainPtr.h>
#include <JavaScriptCore/JSStringRef.h>
+#include <stdio.h>
+
LayoutTestController::~LayoutTestController()
@@ -170,7 +172,7 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled)
// FIXME: implement
}
-void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
+void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
{
// FIXME: implement
}
@@ -360,9 +362,19 @@ void LayoutTestController::disableImageLoading()
}
-void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
{
+ // FIXME: implement
+}
+void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
+{
+ // FIXME: implement
+}
+
+void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
+{
+ // FIXME: implement
}
JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id)
@@ -386,3 +398,36 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data,
{
}
+
+void LayoutTestController::apiTestGoToCurrentBackForwardItem()
+{
+
+}
+
+void LayoutTestController::setSpatialNavigationEnabled(bool)
+{
+
+}
+
+void LayoutTestController::setWebViewEditable(bool)
+{
+}
+
+bool LayoutTestController::callShouldCloseOnWebView()
+{
+ return false;
+}
+
+JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
+{
+ return 0;
+}
+
+JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef, JSValueRef)
+{
+ return 0;
+}
+
+void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef)
+{
+}
diff --git a/WebKitTools/EWSTools/boot.sh b/WebKitTools/EWSTools/boot.sh
new file mode 100644
index 0000000..733441e
--- /dev/null
+++ b/WebKitTools/EWSTools/boot.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Copyright (c) 2010 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.
+
+screen -c ~/tools/screen-config
diff --git a/WebKitTools/EWSTools/create-webkit-git b/WebKitTools/EWSTools/create-webkit-git
new file mode 100644
index 0000000..0088a47
--- /dev/null
+++ b/WebKitTools/EWSTools/create-webkit-git
@@ -0,0 +1,36 @@
+#/bin/bash
+# Copyright (c) 2010 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.
+
+cd /mnt/git
+git clone git://git.webkit.org/WebKit.git webkit
+cd webkit
+git svn init -T trunk http://svn.webkit.org/repository/webkit
+git update-ref refs/remotes/trunk origin/master
+git svn rebase
+
diff --git a/WebKitTools/EWSTools/screen-config b/WebKitTools/EWSTools/screen-config
new file mode 100644
index 0000000..5c003df
--- /dev/null
+++ b/WebKitTools/EWSTools/screen-config
@@ -0,0 +1,4 @@
+screen -t style ./start-queue.sh style-queue
+screen -t qt ./start-queue.sh qt-ews
+screen -t kr ./start-queue.sh chromium-ews
+screen -t gtk ./start-queue.sh gtk-ews
diff --git a/WebKitTools/EWSTools/start-queue.sh b/WebKitTools/EWSTools/start-queue.sh
new file mode 100644
index 0000000..1e0403f
--- /dev/null
+++ b/WebKitTools/EWSTools/start-queue.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Copyright (c) 2010 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.
+
+cd /mnt/git/webkit-$1
+while :
+do
+ git reset --hard
+ git clean -f
+ git svn rebase
+ ./WebKitTools/Scripts/webkit-patch $1 --no-confirm --exit-after-iteration 100
+done
diff --git a/WebKitTools/MiniBrowser/Makefile b/WebKitTools/MiniBrowser/Makefile
new file mode 100644
index 0000000..1f1dbbc
--- /dev/null
+++ b/WebKitTools/MiniBrowser/Makefile
@@ -0,0 +1,2 @@
+SCRIPTS_PATH = ../Scripts
+include ../../Makefile.shared
diff --git a/WebKitTools/MiniBrowser/MiniBrowser.vcproj b/WebKitTools/MiniBrowser/MiniBrowser.vcproj
new file mode 100644
index 0000000..8b956e7
--- /dev/null
+++ b/WebKitTools/MiniBrowser/MiniBrowser.vcproj
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="MiniBrowser"
+ ProjectGUID="{1480CF5F-4160-47B5-A0E6-96AEC8258FB5}"
+ RootNamespace="MiniBrowser"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(WebKitOutputDir)\bin"
+ IntermediateDirectory="$(WebKitOutputDir)\obj\$(ProjectName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops"
+ UseOfATL="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="set PATH=%SystemDrive%\cygwin\bin;%PATH%&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitOutputDir)\buildfailed&quot; grep XX$(ProjectName)XX &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;if errorlevel 1 exit 1&#x0D;&#x0A;echo XX$(ProjectName)XX &gt; &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="&quot;$(WebKitLibrariesDir)\Include&quot;;&quot;$(WebKitOutputDir)\Include&quot;"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(IntDir)\include&quot;;..\..\Internal\Safari\win\WTL80\include"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib WebKit2$(WebKitDLLConfigSuffix).lib CoreFoundation$(LibraryConfigSuffix).lib CFNetwork$(LibraryConfigSuffix).lib"
+ LinkIncremental="2"
+ 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="mkdir 2&gt;NUL &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CFNetwork.resources&quot; &quot;$(WebKitOutputDir)\bin\CFNetwork.resources&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CoreFoundation.resources&quot; &quot;$(WebKitOutputDir)\bin\CoreFoundation.resources&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CharacterSets&quot; &quot;$(WebKitOutputDir)\bin\CharacterSets&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\dnssd.dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icudt40.dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).dll&quot; xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;if exist &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).pdb&quot; xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitOutputDir)\buildfailed&quot; del &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;&#x0D;&#x0A;"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(WebKitOutputDir)\bin"
+ IntermediateDirectory="$(WebKitOutputDir)\obj\$(ProjectName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops"
+ UseOfATL="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="set PATH=%SystemDrive%\cygwin\bin;%PATH%&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitOutputDir)\buildfailed&quot; grep XX$(ProjectName)XX &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;if errorlevel 1 exit 1&#x0D;&#x0A;echo XX$(ProjectName)XX &gt; &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(WebKitLibrariesDir)\Include&quot;;&quot;$(WebKitOutputDir)\Include&quot;"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(IntDir)\include&quot;;..\Safari\win\WTL80\include"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib WebKit2$(WebKitDLLConfigSuffix).lib CoreFoundation$(LibraryConfigSuffix).lib CFNetwork$(LibraryConfigSuffix).lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="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="mkdir 2&gt;NUL &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CFNetwork.resources&quot; &quot;$(WebKitOutputDir)\bin\CFNetwork.resources&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CoreFoundation.resources&quot; &quot;$(WebKitOutputDir)\bin\CoreFoundation.resources&quot;&#x0D;&#x0A;xcopy /y /d /e /i &quot;$(WebKitLibrariesDir)\bin\CharacterSets&quot; &quot;$(WebKitOutputDir)\bin\CharacterSets&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\dnssd.dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icudt40.dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).dll&quot; xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).dll&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;if exist &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).pdb&quot; xcopy /y /d &quot;$(WebKitLibrariesDir)\bin\QuartzCore$(LibraryConfigSuffix).pdb&quot; &quot;$(WebKitOutputDir)\bin&quot;&#x0D;&#x0A;&#x0D;&#x0A;if exist &quot;$(WebKitOutputDir)\buildfailed&quot; del &quot;$(WebKitOutputDir)\buildfailed&quot;&#x0D;&#x0A;&#x0D;&#x0A;"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\win\BrowserView.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\win\BrowserWindow.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\win\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\win\MiniBrowser.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\win\stdafx.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\win\BrowserView.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win\BrowserWindow.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win\MiniBrowser.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win\Resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win\stdafx.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\win\MiniBrowser.rc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/WebKitTools/MiniBrowser/MiniBrowser.xcodeproj/project.pbxproj b/WebKitTools/MiniBrowser/MiniBrowser.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..6c5b9a1
--- /dev/null
+++ b/WebKitTools/MiniBrowser/MiniBrowser.xcodeproj/project.pbxproj
@@ -0,0 +1,304 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
+ 256AC3DA0F4B6AC300CF3369 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* AppDelegate.m */; };
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ BC329487116A92E2008635D0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BC329486116A92E2008635D0 /* main.m */; };
+ BC329498116A941B008635D0 /* BrowserWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC329497116A941B008635D0 /* BrowserWindowController.m */; };
+ BC3294A3116A9852008635D0 /* BrowserWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC3294A1116A9852008635D0 /* BrowserWindow.xib */; };
+ BC8FB5A8116AA1FE0080D413 /* WebKit2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC8FB5A7116AA1FE0080D413 /* WebKit2.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 089C165DFE840E0CC02AAC07 /* InfoPlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = mac/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+ 1DDD58150DA1D0A300B32029 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = mac/English.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 256AC3D80F4B6AC300CF3369 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = mac/AppDelegate.h; sourceTree = "<group>"; };
+ 256AC3D90F4B6AC300CF3369 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = mac/AppDelegate.m; sourceTree = "<group>"; };
+ 256AC3F00F4B6AF500CF3369 /* MiniBrowser_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MiniBrowser_Prefix.pch; path = mac/MiniBrowser_Prefix.pch; sourceTree = "<group>"; };
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 8D1107310486CEB800E47090 /* MiniBrowser-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "MiniBrowser-Info.plist"; path = "mac/MiniBrowser-Info.plist"; sourceTree = "<group>"; };
+ 8D1107320486CEB800E47090 /* MiniBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MiniBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ BC329486116A92E2008635D0 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = mac/main.m; sourceTree = "<group>"; };
+ BC329496116A941B008635D0 /* BrowserWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BrowserWindowController.h; path = mac/BrowserWindowController.h; sourceTree = "<group>"; };
+ BC329497116A941B008635D0 /* BrowserWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BrowserWindowController.m; path = mac/BrowserWindowController.m; sourceTree = "<group>"; };
+ BC3294A2116A9852008635D0 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = mac/English.lproj/BrowserWindow.xib; sourceTree = "<group>"; };
+ BC8FB5A7116AA1FE0080D413 /* WebKit2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WebKit2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
+ BC8FB5A8116AA1FE0080D413 /* WebKit2.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 256AC3D80F4B6AC300CF3369 /* AppDelegate.h */,
+ 256AC3D90F4B6AC300CF3369 /* AppDelegate.m */,
+ BC329496116A941B008635D0 /* BrowserWindowController.h */,
+ BC329497116A941B008635D0 /* BrowserWindowController.m */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ BC8FB5A7116AA1FE0080D413 /* WebKit2.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107320486CEB800E47090 /* MiniBrowser.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* MiniBrowser */ = {
+ isa = PBXGroup;
+ children = (
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = MiniBrowser;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 256AC3F00F4B6AF500CF3369 /* MiniBrowser_Prefix.pch */,
+ BC329486116A92E2008635D0 /* main.m */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107310486CEB800E47090 /* MiniBrowser-Info.plist */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ BC3294A1116A9852008635D0 /* BrowserWindow.xib */,
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D1107260486CEB800E47090 /* MiniBrowser */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "MiniBrowser" */;
+ buildPhases = (
+ 8D1107290486CEB800E47090 /* Resources */,
+ 8D11072C0486CEB800E47090 /* Sources */,
+ 8D11072E0486CEB800E47090 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = MiniBrowser;
+ productInstallPath = "$(HOME)/Applications";
+ productName = MiniBrowser;
+ productReference = 8D1107320486CEB800E47090 /* MiniBrowser.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MiniBrowser" */;
+ compatibilityVersion = "Xcode 3.1";
+ hasScannedForEncodings = 1;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* MiniBrowser */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8D1107260486CEB800E47090 /* MiniBrowser */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D1107290486CEB800E47090 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
+ BC3294A3116A9852008635D0 /* BrowserWindow.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D11072C0486CEB800E47090 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 256AC3DA0F4B6AC300CF3369 /* AppDelegate.m in Sources */,
+ BC329487116A92E2008635D0 /* main.m in Sources */,
+ BC329498116A941B008635D0 /* BrowserWindowController.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* InfoPlist.strings */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 1DDD58150DA1D0A300B32029 /* MainMenu.xib */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+ BC3294A1116A9852008635D0 /* BrowserWindow.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ BC3294A2116A9852008635D0 /* English */,
+ );
+ name = BrowserWindow.xib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ C01FCF4B08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = mac/MiniBrowser_Prefix.pch;
+ INFOPLIST_FILE = "mac/MiniBrowser-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = MiniBrowser;
+ };
+ name = Debug;
+ };
+ C01FCF4C08A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = mac/MiniBrowser_Prefix.pch;
+ INFOPLIST_FILE = "mac/MiniBrowser-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = MiniBrowser;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.6;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.6;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "MiniBrowser" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4B08A954540054247B /* Debug */,
+ C01FCF4C08A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MiniBrowser" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/WebKitTools/MiniBrowser/mac/AppDelegate.h b/WebKitTools/MiniBrowser/mac/AppDelegate.h
new file mode 100644
index 0000000..79683f3
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/AppDelegate.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <WebKit2/WebKit2.h>
+
+@interface BrowserAppDelegate : NSObject <NSApplicationDelegate> {
+ WKProcessModel currentProcessModel;
+ WKPageNamespaceRef pageNamespace;
+}
+
+- (WKPageNamespaceRef)getPageNamespace;
+
+- (IBAction)setSharedProcessProcessModel:(id)sender;
+- (IBAction)setSharedThreadProcessModel:(id)sender;
+
+@end
diff --git a/WebKitTools/MiniBrowser/mac/AppDelegate.m b/WebKitTools/MiniBrowser/mac/AppDelegate.m
new file mode 100644
index 0000000..e10deb1
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/AppDelegate.m
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#import "AppDelegate.h"
+#import "BrowserWindowController.h"
+
+static NSString *defaultURL = @"http://webkit.org/";
+static const WKProcessModel defaultProcessModel = kWKProcessModelSecondaryProcess;
+
+@implementation BrowserAppDelegate
+
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ if ([NSEvent modifierFlags] & NSShiftKeyMask)
+ currentProcessModel = kWKProcessModelSecondaryThread;
+ else
+ currentProcessModel = kWKProcessModelSecondaryProcess;
+ }
+
+ return self;
+}
+
+- (IBAction)newWindow:(id)sender
+{
+ BrowserWindowController *controller = [[BrowserWindowController alloc] initWithPageNamespace:[self getPageNamespace]];
+ [[controller window] makeKeyAndOrderFront:sender];
+
+ [controller loadURLString:defaultURL];
+}
+
+- (WKPageNamespaceRef)getPageNamespace
+{
+ if (!pageNamespace) {
+ WKContextRef context = WKContextCreateWithProcessModel(currentProcessModel);
+ pageNamespace = WKPageNamespaceCreate(context);
+ WKContextRelease(context);
+ }
+
+ return pageNamespace;
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(setSharedProcessProcessModel:))
+ [menuItem setState:currentProcessModel == kWKProcessModelSecondaryProcess ? NSOnState : NSOffState];
+ else if ([menuItem action] == @selector(setSharedThreadProcessModel:))
+ [menuItem setState:currentProcessModel == kWKProcessModelSecondaryThread ? NSOnState : NSOffState];
+ return YES;
+}
+
+- (void)_setProcessModel:(WKProcessModel)processModel
+{
+ if (processModel == currentProcessModel)
+ return;
+
+ currentProcessModel = processModel;
+ if (pageNamespace) {
+ WKPageNamespaceRelease(pageNamespace);
+ pageNamespace = 0;
+ }
+}
+
+- (IBAction)setSharedProcessProcessModel:(id)sender
+{
+ [self _setProcessModel:kWKProcessModelSecondaryProcess];
+}
+
+- (IBAction)setSharedThreadProcessModel:(id)sender
+{
+ [self _setProcessModel:kWKProcessModelSecondaryThread];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ [self newWindow:self];
+}
+
+- (void)applicationWillTerminate:(NSNotification *)notification
+{
+ NSArray* windows = [NSApp windows];
+ for (NSWindow* window in windows) {
+ BrowserWindowController *controller = (BrowserWindowController *)[window delegate];
+ [controller applicationTerminating];
+ }
+
+ if (pageNamespace) {
+ WKPageNamespaceRelease(pageNamespace);
+ pageNamespace = 0;
+ }
+}
+
+@end
diff --git a/WebKitTools/MiniBrowser/mac/BrowserWindowController.h b/WebKitTools/MiniBrowser/mac/BrowserWindowController.h
new file mode 100644
index 0000000..f817e89
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/BrowserWindowController.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import <WebKit2/WebKit2.h>
+
+@interface BrowserWindowController : NSWindowController {
+ IBOutlet NSProgressIndicator *progressIndicator;
+ IBOutlet NSButton *reloadButton;
+ IBOutlet NSTextField *urlText;
+ IBOutlet NSView *containerView;
+
+ WKPageNamespaceRef _pageNamespace;
+ WKView *_webView;
+}
+- (IBAction)fetch:(id)sender;
+- (IBAction)reload:(id)sender;
+- (IBAction)forceRepaint:(id)sender;
+
+- (IBAction)showHideWebView:(id)sender;
+- (IBAction)removeReinsertWebView:(id)sender;
+
+- (id)initWithPageNamespace:(WKPageNamespaceRef)pageNamespace;
+- (void)loadURLString:(NSString *)urlString;
+- (void)applicationTerminating;
+
+@end
diff --git a/WebKitTools/MiniBrowser/mac/BrowserWindowController.m b/WebKitTools/MiniBrowser/mac/BrowserWindowController.m
new file mode 100644
index 0000000..b9306c0
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/BrowserWindowController.m
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#import "BrowserWindowController.h"
+
+#import <WebKit2/WKStringCF.h>
+#import <WebKit2/WKURLCF.h>
+
+@interface BrowserWindowController ()
+- (void)didStartProgress;
+- (void)didChangeProgress:(double)value;
+- (void)didFinishProgress;
+@end
+
+@implementation BrowserWindowController
+
+- (id)initWithPageNamespace:(WKPageNamespaceRef)pageNamespace
+{
+ if (self = [super initWithWindowNibName:@"BrowserWindow"])
+ _pageNamespace = WKPageNamespaceRetain(pageNamespace);
+
+ return self;
+}
+
+- (void)dealloc
+{
+ assert(!_pageNamespace);
+ [super dealloc];
+}
+
+- (IBAction)fetch:(id)sender
+{
+ CFURLRef cfURL = CFURLCreateWithString(0, (CFStringRef)[urlText stringValue], 0);
+ WKURLRef url = WKURLCreateWithCFURL(cfURL);
+ CFRelease(cfURL);
+
+ WKPageLoadURL(_webView.pageRef, url);
+ WKURLRelease(url);
+}
+
+- (IBAction)showHideWebView:(id)sender
+{
+ BOOL hidden = ![_webView isHidden];
+
+ [_webView setHidden:hidden];
+}
+
+- (IBAction)removeReinsertWebView:(id)sender
+{
+ if ([_webView window]) {
+ [_webView retain];
+ [_webView removeFromSuperview];
+ } else {
+ [containerView addSubview:_webView];
+ [_webView release];
+ }
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(showHideWebView:))
+ [menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
+ else if ([menuItem action] == @selector(removeReinsertWebView:))
+ [menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
+ return YES;
+}
+
+- (IBAction)reload:(id)sender
+{
+ WKPageReload(_webView.pageRef);
+}
+
+- (IBAction)forceRepaint:(id)sender
+{
+ [_webView setNeedsDisplay:YES];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ NSLog(@"windowShouldClose");
+ BOOL canCloseImmediately = WKPageTryClose(_webView.pageRef);
+ return canCloseImmediately;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ WKPageNamespaceRelease(_pageNamespace);
+ _pageNamespace = 0;
+}
+
+- (void)applicationTerminating
+{
+ WKPageClose(_webView.pageRef);
+ WKPageRelease(_webView.pageRef);
+}
+
+#pragma mark Loader Client Callbacks
+
+static void _didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didStartProvisionalLoadForFrame");
+}
+
+static void _didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didReceiveServerRedirectForProvisionalLoadForFrame");
+}
+
+static void _didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didFailProvisionalLoadWithErrorForFrame");
+}
+
+static void _didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didCommitLoadForFrame");
+}
+
+static void _didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didFinishLoadForFrame");
+}
+
+static void _didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didFailLoadWithErrorForFrame");
+}
+
+static void _didReceiveTitleForFrame(WKPageRef page, WKStringRef title, WKFrameRef frame, const void *clientInfo)
+{
+ CFStringRef cfTitle = WKStringCopyCFString(0, title);
+ NSLog(@"didReceiveTitleForFrame \"%@\"", (NSString *)cfTitle);
+ CFRelease(cfTitle);
+}
+
+static void _didFirstLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didFirstLayoutForFrame");
+}
+
+static void _didFirstVisuallyNonEmptyLayoutForFrame(WKPageRef page, WKFrameRef frame, const void *clientInfo)
+{
+ NSLog(@"didFirstVisuallyNonEmptyLayoutForFrame");
+}
+
+static void _didStartProgress(WKPageRef page, const void *clientInfo)
+{
+ [(BrowserWindowController *)clientInfo didStartProgress];
+}
+
+static void _didChangeProgress(WKPageRef page, double value, const void *clientInfo)
+{
+ [(BrowserWindowController *)clientInfo didChangeProgress:value];
+}
+
+static void _didFinishProgress(WKPageRef page, const void *clientInfo)
+{
+ [(BrowserWindowController *)clientInfo didFinishProgress];
+}
+
+static void _didBecomeUnresponsive(WKPageRef page, const void *clientInfo)
+{
+ NSLog(@"didBecomeUnresponsive");
+}
+
+static void _didBecomeResponsive(WKPageRef page, const void *clientInfo)
+{
+ NSLog(@"didBecomeResponsive");
+}
+
+#pragma mark Policy Client Callbacks
+
+static void _decidePolicyForNavigationAction(WKPageRef page, uint32_t navigationType, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
+{
+ NSLog(@"decidePolicyForNavigationAction");
+ WKFramePolicyListenerUse(listener);
+}
+
+static void _decidePolicyForNewWindowAction(WKPageRef page, uint32_t navigationType, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
+{
+ NSLog(@"decidePolicyForNewWindowAction");
+ WKFramePolicyListenerUse(listener);
+}
+
+static void _decidePolicyForMIMEType(WKPageRef page, WKStringRef MIMEType, WKURLRef url, WKFrameRef frame, WKFramePolicyListenerRef listener, const void *clientInfo)
+{
+ WKFramePolicyListenerUse(listener);
+}
+
+#pragma mark UI Client Callbacks
+
+static WKPageRef _createNewPage(WKPageRef page, const void* clientInfo)
+{
+ NSLog(@"createNewPage");
+ BrowserWindowController *controller = [[BrowserWindowController alloc] initWithPageNamespace:WKPageGetPageNamespace(page)];
+ [controller loadWindow];
+
+ return controller->_webView.pageRef;
+}
+
+static void _showPage(WKPageRef page, const void *clientInfo)
+{
+ NSLog(@"showPage");
+ [[(BrowserWindowController *)clientInfo window] orderFront:nil];
+}
+
+static void _closePage(WKPageRef page, const void *clientInfo)
+{
+ NSLog(@"closePage");
+ WKPageClose(page);
+ [[(BrowserWindowController *)clientInfo window] close];
+ WKPageRelease(page);
+}
+
+static void _runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, const void* clientInfo)
+{
+ NSAlert* alert = [[NSAlert alloc] init];
+
+ CFURLRef cfURL = WKURLCopyCFURL(0, WKFrameGetURL(frame));
+ [alert setMessageText:[NSString stringWithFormat:@"JavaScript Alert from %@.", [(NSURL *)cfURL absoluteString]]];
+ CFRelease(cfURL);
+
+ CFStringRef cfAlertText = WKStringCopyCFString(0, alertText);
+ [alert setInformativeText:(NSString *)alertText];
+ CFRelease(cfAlertText);
+
+ [alert addButtonWithTitle:@"OK"];
+
+ [alert runModal];
+ [alert release];
+}
+
+
+#pragma mark History Client Callbacks
+
+static void _didNavigateWithNavigationData(WKPageRef page, WKNavigationDataRef navigationData, WKFrameRef frame, const void *clientInfo)
+{
+ CFStringRef title = WKStringCopyCFString(0, WKNavigationDataGetTitle(navigationData));
+ CFURLRef url = WKURLCopyCFURL(0, WKNavigationDataGetURL(navigationData));
+ NSLog(@"HistoryClient - didNavigateWithNavigationData - title: %@ - url: %@", title, url);
+ CFRelease(title);
+ CFRelease(url);
+}
+
+static void _didPerformClientRedirect(WKPageRef page, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void *clientInfo)
+{
+ CFURLRef cfSourceURL = WKURLCopyCFURL(0, sourceURL);
+ CFURLRef cfDestinationURL = WKURLCopyCFURL(0, destinationURL);
+ NSLog(@"HistoryClient - didPerformClientRedirect - sourceURL: %@ - destinationURL: %@", cfSourceURL, cfDestinationURL);
+ CFRelease(cfSourceURL);
+ CFRelease(cfDestinationURL);
+}
+
+static void _didPerformServerRedirect(WKPageRef page, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void *clientInfo)
+{
+ CFURLRef cfSourceURL = WKURLCopyCFURL(0, sourceURL);
+ CFURLRef cfDestinationURL = WKURLCopyCFURL(0, destinationURL);
+ NSLog(@"HistoryClient - didPerformServerRedirect - sourceURL: %@ - destinationURL: %@", cfSourceURL, cfDestinationURL);
+ CFRelease(cfSourceURL);
+ CFRelease(cfDestinationURL);
+}
+
+static void _didUpdateHistoryTitle(WKPageRef page, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void *clientInfo)
+{
+ CFStringRef cfTitle = WKStringCopyCFString(0, title);
+ CFURLRef cfURL = WKURLCopyCFURL(0, URL);
+ NSLog(@"HistoryClient - didUpdateHistoryTitle - title: %@ - URL: %@", cfTitle, cfURL);
+ CFRelease(cfTitle);
+ CFRelease(cfURL);
+}
+
+- (void)awakeFromNib
+{
+ _webView = [[WKView alloc] initWithFrame:[containerView frame] pageNamespaceRef:_pageNamespace];
+
+ [containerView addSubview:_webView];
+ [_webView setFrame:[containerView frame]];
+
+ [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+
+ WKPageLoaderClient loadClient = {
+ 0, /* version */
+ self, /* clientInfo */
+ _didStartProvisionalLoadForFrame,
+ _didReceiveServerRedirectForProvisionalLoadForFrame,
+ _didFailProvisionalLoadWithErrorForFrame,
+ _didCommitLoadForFrame,
+ _didFinishLoadForFrame,
+ _didFailLoadWithErrorForFrame,
+ _didReceiveTitleForFrame,
+ _didFirstLayoutForFrame,
+ _didFirstVisuallyNonEmptyLayoutForFrame,
+ _didStartProgress,
+ _didChangeProgress,
+ _didFinishProgress,
+ _didBecomeUnresponsive,
+ _didBecomeResponsive
+ };
+ WKPageSetPageLoaderClient(_webView.pageRef, &loadClient);
+
+ WKPagePolicyClient policyClient = {
+ 0, /* version */
+ self, /* clientInfo */
+ _decidePolicyForNavigationAction,
+ _decidePolicyForNewWindowAction,
+ _decidePolicyForMIMEType
+ };
+ WKPageSetPagePolicyClient(_webView.pageRef, &policyClient);
+
+ WKPageUIClient uiClient = {
+ 0, /* version */
+ self, /* clientInfo */
+ _createNewPage,
+ _showPage,
+ _closePage,
+ _runJavaScriptAlert
+ };
+ WKPageSetPageUIClient(_webView.pageRef, &uiClient);
+
+ WKPageHistoryClient historyClient = {
+ 0,
+ self,
+ _didNavigateWithNavigationData,
+ _didPerformClientRedirect,
+ _didPerformServerRedirect,
+ _didUpdateHistoryTitle,
+ };
+
+ WKPageSetPageHistoryClient(_webView.pageRef, &historyClient);
+}
+
+- (void)didStartProgress
+{
+ [progressIndicator setDoubleValue:0.0];
+ [progressIndicator setHidden:NO];
+}
+
+- (void)didChangeProgress:(double)value
+{
+ [progressIndicator setDoubleValue:value];
+}
+
+- (void)didFinishProgress
+{
+ [progressIndicator setHidden:YES];
+ [progressIndicator setDoubleValue:1.0];
+}
+
+- (void)loadURLString:(NSString *)urlString
+{
+ // FIXME: We shouldn't have to set the url text here.
+ [urlText setStringValue:urlString];
+ [self fetch:nil];
+}
+
+@end
diff --git a/WebKitTools/MiniBrowser/mac/English.lproj/BrowserWindow.xib b/WebKitTools/MiniBrowser/mac/English.lproj/BrowserWindow.xib
new file mode 100644
index 0000000..dbd4344
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/English.lproj/BrowserWindow.xib
@@ -0,0 +1,869 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">10D573</string>
+ <string key="IBDocument.InterfaceBuilderVersion">740</string>
+ <string key="IBDocument.AppKitVersion">1038.29</string>
+ <string key="IBDocument.HIToolboxVersion">460.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">740</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">BrowserWindowController</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">4111</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{517, 280}, {776, 658}}</string>
+ <int key="NSWTFlags">544735232</int>
+ <string key="NSWindowTitle">Window</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomView" id="877383975">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrameSize">{776, 608}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <int key="NSViewLayerContentsRedrawPolicy">2</int>
+ <string key="NSClassName">NSView</string>
+ </object>
+ <object class="NSTextField" id="690456651">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{45, 618}, {699, 22}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <int key="NSViewLayerContentsRedrawPolicy">2</int>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="1023147716">
+ <int key="NSCellFlags">-1804468671</int>
+ <int key="NSCellFlags2">268436480</int>
+ <string key="NSContents"/>
+ <object class="NSFont" key="NSSupport" id="1064395332">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="690456651"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="365730878">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSProgressIndicator" id="128750774">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">1289</int>
+ <object class="NSPSMatrix" key="NSDrawMatrix"/>
+ <string key="NSFrame">{{752, 621}, {16, 16}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <int key="NSpiFlags">28936</int>
+ <double key="NSMaxValue">1</double>
+ </object>
+ <object class="NSButton" id="35464578">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{8, 616}, {29, 25}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="366486485">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="1064395332"/>
+ <reference key="NSControlView" ref="35464578"/>
+ <int key="NSButtonFlags">-2033434369</int>
+ <int key="NSButtonFlags2">163</int>
+ <object class="NSCustomResource" key="NSNormalImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSRefreshTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{776, 658}</string>
+ <reference key="NSSuperview"/>
+ <int key="NSViewLayerContentsRedrawPolicy">2</int>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+ <string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">3</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">4</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">urlText</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="690456651"/>
+ </object>
+ <int key="connectionID">32</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">progressIndicator</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="128750774"/>
+ </object>
+ <int key="connectionID">33</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">reloadButton</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="35464578"/>
+ </object>
+ <int key="connectionID">34</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">reload:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="35464578"/>
+ </object>
+ <int key="connectionID">35</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">fetch:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="690456651"/>
+ </object>
+ <int key="connectionID">36</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">containerView</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="877383975"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="690456651"/>
+ <reference ref="128750774"/>
+ <reference ref="35464578"/>
+ <reference ref="877383975"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="690456651"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1023147716"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="1023147716"/>
+ <reference key="parent" ref="690456651"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">21</int>
+ <reference key="object" ref="128750774"/>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="35464578"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="366486485"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="366486485"/>
+ <reference key="parent" ref="35464578"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="877383975"/>
+ <reference key="parent" ref="1006"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>21.IBPluginDependency</string>
+ <string>21.IBViewIntegration.shadowBlurRadius</string>
+ <string>21.IBViewIntegration.shadowColor</string>
+ <string>21.IBViewIntegration.shadowOffsetHeight</string>
+ <string>21.IBViewIntegration.shadowOffsetWidth</string>
+ <string>23.IBPluginDependency</string>
+ <string>24.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{{308, 154}, {776, 658}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{308, 154}, {776, 658}}</string>
+ <integer value="1"/>
+ <string>{196, 240}</string>
+ <string>{{202, 428}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <real value="0.0"/>
+ <reference ref="365730878"/>
+ <real value="0.0"/>
+ <real value="0.0"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">37</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>fetch:</string>
+ <string>reload:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>containerView</string>
+ <string>progressIndicator</string>
+ <string>reloadButton</string>
+ <string>urlText</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSView</string>
+ <string>NSProgressIndicator</string>
+ <string>NSButton</string>
+ <string>NSTextField</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">mac/BrowserWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSActionCell</string>
+ <string key="superclassName">NSCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSActionCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="558771426">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSApplication.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="344596456">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSApplicationScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="428409299">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSColorPanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSHelpManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSPageLayout.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSUserInterfaceItemSearching.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSButton</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSButton.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSButtonCell</string>
+ <string key="superclassName">NSActionCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSButtonCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSCell</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSControl</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="297186634">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSFormatter</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenu</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="808053469">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMenu.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSAccessibility.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="558771426"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="344596456"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="428409299"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="297186634"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDictionaryController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDragging.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSFontManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSFontPanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSKeyValueBinding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="808053469"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSNibLoading.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSOutlineView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSPasteboard.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSSavePanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSToolbarItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="555757547">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSProgressIndicator</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSProgressIndicator.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSInterfaceStyle.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSResponder</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextField</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTextField.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextFieldCell</string>
+ <string key="superclassName">NSActionCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTextFieldCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSClipView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSRulerView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <string key="superclassName">NSResponder</string>
+ <reference key="sourceIdentifier" ref="555757547"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDrawer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindow.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindowScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindowController</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">showWindow:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <integer value="1060" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../MiniBrowser.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/WebKitTools/MiniBrowser/mac/English.lproj/InfoPlist.strings b/WebKitTools/MiniBrowser/mac/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/English.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/WebKitTools/MiniBrowser/mac/English.lproj/MainMenu.xib b/WebKitTools/MiniBrowser/mac/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..ca3010c
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/English.lproj/MainMenu.xib
@@ -0,0 +1,4603 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1060</int>
+ <string key="IBDocument.SystemVersion">10D573</string>
+ <string key="IBDocument.InterfaceBuilderVersion">783</string>
+ <string key="IBDocument.AppKitVersion">1038.29</string>
+ <string key="IBDocument.HIToolboxVersion">460.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">783</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="535"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">MiniBrowser</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="35465992">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="502551668">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">MiniBrowser</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About MiniBrowser</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Preferences…</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Services</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <string key="NSTitle">Services</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide MiniBrowser</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit MiniBrowser</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <string key="NSTitle">File</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New Window</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open…</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1025936716">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open Recent</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1065607017">
+ <string key="NSTitle">Open Recent</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="759406840">
+ <reference key="NSMenu" ref="1065607017"/>
+ <string key="NSTitle">Clear Menu</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSRecentDocumentsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1023925487">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="117038363">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save As…</string>
+ <string key="NSKeyEquiv">S</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="579971712">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Revert to Saved</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSToolTip"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Print…</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="789758025">
+ <string key="NSTitle">Edit</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1058277027">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="790794224">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1040322652">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="296257095">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="860595796">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="29853731">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="82994268">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste and Match Style</string>
+ <string key="NSKeyEquiv">V</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="437104165">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Delete</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="583158037">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="212016141">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="892235320">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Find</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="963351320">
+ <string key="NSTitle">Find</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="447796847">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find…</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="326711663">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="270902937">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="159080638">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">7</int>
+ </object>
+ <object class="NSMenuItem" id="88285865">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Jump to Selection</string>
+ <string key="NSKeyEquiv">j</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="972420730">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Spelling and Grammar</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="769623530">
+ <string key="NSTitle">Spelling and Grammar</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="679648819">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Show Spelling and Grammar</string>
+ <string key="NSKeyEquiv">:</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="96193923">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Document Now</string>
+ <string key="NSKeyEquiv">;</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="859480356">
+ <reference key="NSMenu" ref="769623530"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="948374510">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling While Typing</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="967646866">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Grammar With Spelling</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="795346622">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Correct Spelling Automatically</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="507821607">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="698887838">
+ <string key="NSTitle">Substitutions</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="65139061">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Show Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="19036812">
+ <reference key="NSMenu" ref="698887838"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="605118523">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Copy/Paste</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="197661976">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Quotes</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="672708820">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Dashes</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="708854459">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Links</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="537092702">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Text Replacement</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="288088188">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Transformations</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="579392910">
+ <string key="NSTitle">Transformations</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1060694897">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Make Upper Case</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="879586729">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Make Lower Case</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="56570060">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Capitalize</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="676164635">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Speech</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="785027613">
+ <string key="NSTitle">Speech</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="731782645">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Start Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="680220178">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="302598603">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Format</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="941447902">
+ <string key="NSTitle">Format</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="792887677">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Font</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="786677654">
+ <string key="NSTitle">Font</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="159677712">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Fonts</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="305399458">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bold</string>
+ <string key="NSKeyEquiv">b</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="814362025">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Italic</string>
+ <string key="NSKeyEquiv">i</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="330926929">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Underline</string>
+ <string key="NSKeyEquiv">u</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="533507878">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="158063935">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bigger</string>
+ <string key="NSKeyEquiv">+</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="885547335">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Smaller</string>
+ <string key="NSKeyEquiv">-</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">4</int>
+ </object>
+ <object class="NSMenuItem" id="901062459">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="767671776">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Kern</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="175441468">
+ <string key="NSTitle">Kern</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="252969304">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="766922938">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="677519740">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Tighten</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="238351151">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Loosen</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="691570813">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Ligature</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1058217995">
+ <string key="NSTitle">Ligature</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="706297211">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="568384683">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="663508465">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="769124883">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Baseline</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="18263474">
+ <string key="NSTitle">Baseline</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="257962622">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="644725453">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Superscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1037576581">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Subscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="941806246">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Raise</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1045724900">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Lower</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="739652853">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1012600125">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Colors</string>
+ <string key="NSKeyEquiv">C</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="214559597">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="596732606">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Copy Style</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="393423671">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Paste Style</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSFontMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="215659978">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Text</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="446991534">
+ <string key="NSTitle">Text</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="875092757">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Align Left</string>
+ <string key="NSKeyEquiv">{</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="630155264">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Center</string>
+ <string key="NSKeyEquiv">|</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="945678886">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Justify</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="512868991">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Align Right</string>
+ <string key="NSKeyEquiv">}</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="163117631">
+ <reference key="NSMenu" ref="446991534"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="31516759">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Writing Direction</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="956096989">
+ <string key="NSTitle">Writing Direction</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="257099033">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <string key="NSTitle">Paragraph</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="551969625">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="249532473">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="607364498">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="508151438">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="981751889">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <string key="NSTitle">Selection</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="380031999">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="825984362">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="560145579">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="908105787">
+ <reference key="NSMenu" ref="446991534"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="644046920">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Show Ruler</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="231811626">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Copy Ruler</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="883618387">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Paste Ruler</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <string key="NSTitle">View</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Customize Toolbar…</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Window</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="835318025">
+ <string key="NSTitle">Window</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1011231497">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Minimize</string>
+ <string key="NSKeyEquiv">m</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="575023229">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Zoom</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="299356726">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="625202149">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Bring All to Front</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSWindowsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="448692316">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="992780483">
+ <string key="NSTitle">Help</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="105068016">
+ <reference key="NSMenu" ref="992780483"/>
+ <string key="NSTitle">MiniBrowser Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSHelpMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="816668511">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Debug</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="865232259">
+ <string key="NSTitle">Debug</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="878165919">
+ <reference key="NSMenu" ref="865232259"/>
+ <string key="NSTitle">Force Repaint</string>
+ <string key="NSKeyEquiv">r</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="137933275">
+ <reference key="NSMenu" ref="865232259"/>
+ <string key="NSTitle">Hide Web View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1027125810">
+ <reference key="NSMenu" ref="865232259"/>
+ <string key="NSTitle">Remove Web View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="162978490">
+ <reference key="NSMenu" ref="865232259"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="340689355">
+ <reference key="NSMenu" ref="865232259"/>
+ <string key="NSTitle">Process Model</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="935112943">
+ <string key="NSTitle">Process Model</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="993856752">
+ <reference key="NSMenu" ref="935112943"/>
+ <string key="NSTitle">Shared Process</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="516840223">
+ <reference key="NSMenu" ref="935112943"/>
+ <string key="NSTitle">Shared Thread</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ <object class="NSCustomObject" id="976324537">
+ <string key="NSClassName">BrowserAppDelegate</string>
+ </object>
+ <object class="NSCustomObject" id="755631768">
+ <string key="NSClassName">NSFontManager</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performMiniaturize:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1011231497"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">arrangeInFront:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="625202149"/>
+ </object>
+ <int key="connectionID">39</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">clearRecentDocuments:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="759406840"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">193</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleContinuousSpellChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="948374510"/>
+ </object>
+ <int key="connectionID">222</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1058277027"/>
+ </object>
+ <int key="connectionID">223</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="860595796"/>
+ </object>
+ <int key="connectionID">224</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">checkSpelling:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="96193923"/>
+ </object>
+ <int key="connectionID">225</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="29853731"/>
+ </object>
+ <int key="connectionID">226</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="680220178"/>
+ </object>
+ <int key="connectionID">227</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="296257095"/>
+ </object>
+ <int key="connectionID">228</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGuessPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="679648819"/>
+ </object>
+ <int key="connectionID">230</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="790794224"/>
+ </object>
+ <int key="connectionID">231</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="583158037"/>
+ </object>
+ <int key="connectionID">232</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="731782645"/>
+ </object>
+ <int key="connectionID">233</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="437104165"/>
+ </object>
+ <int key="connectionID">235</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performZoom:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="575023229"/>
+ </object>
+ <int key="connectionID">240</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="447796847"/>
+ </object>
+ <int key="connectionID">241</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">centerSelectionInVisibleArea:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="88285865"/>
+ </object>
+ <int key="connectionID">245</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleGrammarChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="967646866"/>
+ </object>
+ <int key="connectionID">347</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleSmartInsertDelete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="605118523"/>
+ </object>
+ <int key="connectionID">355</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticQuoteSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197661976"/>
+ </object>
+ <int key="connectionID">356</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticLinkDetection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="708854459"/>
+ </object>
+ <int key="connectionID">357</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1023925487"/>
+ </object>
+ <int key="connectionID">362</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocumentAs:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="117038363"/>
+ </object>
+ <int key="connectionID">363</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">revertDocumentToSaved:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="579971712"/>
+ </object>
+ <int key="connectionID">364</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">367</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">368</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">374</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="305399458"/>
+ </object>
+ <int key="connectionID">421</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="814362025"/>
+ </object>
+ <int key="connectionID">422</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="885547335"/>
+ </object>
+ <int key="connectionID">423</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontFontPanel:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="159677712"/>
+ </object>
+ <int key="connectionID">424</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="158063935"/>
+ </object>
+ <int key="connectionID">425</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">raiseBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="941806246"/>
+ </object>
+ <int key="connectionID">426</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">lowerBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1045724900"/>
+ </object>
+ <int key="connectionID">427</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="596732606"/>
+ </object>
+ <int key="connectionID">428</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">subscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1037576581"/>
+ </object>
+ <int key="connectionID">429</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">superscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="644725453"/>
+ </object>
+ <int key="connectionID">430</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">tightenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="677519740"/>
+ </object>
+ <int key="connectionID">431</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">underline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="330926929"/>
+ </object>
+ <int key="connectionID">432</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontColorPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1012600125"/>
+ </object>
+ <int key="connectionID">433</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useAllLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="663508465"/>
+ </object>
+ <int key="connectionID">434</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">loosenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238351151"/>
+ </object>
+ <int key="connectionID">435</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="393423671"/>
+ </object>
+ <int key="connectionID">436</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="257962622"/>
+ </object>
+ <int key="connectionID">437</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="252969304"/>
+ </object>
+ <int key="connectionID">438</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="706297211"/>
+ </object>
+ <int key="connectionID">439</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="568384683"/>
+ </object>
+ <int key="connectionID">440</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="766922938"/>
+ </object>
+ <int key="connectionID">441</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">449</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticSpellingCorrection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="795346622"/>
+ </object>
+ <int key="connectionID">456</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontSubstitutionsPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="65139061"/>
+ </object>
+ <int key="connectionID">458</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticDashSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="672708820"/>
+ </object>
+ <int key="connectionID">461</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticTextReplacement:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="537092702"/>
+ </object>
+ <int key="connectionID">463</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">uppercaseWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1060694897"/>
+ </object>
+ <int key="connectionID">464</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">capitalizeWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="56570060"/>
+ </object>
+ <int key="connectionID">467</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">lowercaseWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="879586729"/>
+ </object>
+ <int key="connectionID">468</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteAsPlainText:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="82994268"/>
+ </object>
+ <int key="connectionID">486</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="326711663"/>
+ </object>
+ <int key="connectionID">487</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="270902937"/>
+ </object>
+ <int key="connectionID">488</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="159080638"/>
+ </object>
+ <int key="connectionID">489</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="105068016"/>
+ </object>
+ <int key="connectionID">493</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="976324537"/>
+ </object>
+ <int key="connectionID">495</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignCenter:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="630155264"/>
+ </object>
+ <int key="connectionID">518</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="883618387"/>
+ </object>
+ <int key="connectionID">519</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="644046920"/>
+ </object>
+ <int key="connectionID">520</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="512868991"/>
+ </object>
+ <int key="connectionID">521</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="231811626"/>
+ </object>
+ <int key="connectionID">522</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignJustified:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="945678886"/>
+ </object>
+ <int key="connectionID">523</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="875092757"/>
+ </object>
+ <int key="connectionID">524</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionNatural:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="551969625"/>
+ </object>
+ <int key="connectionID">525</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionLeftToRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="249532473"/>
+ </object>
+ <int key="connectionID">526</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionRightToLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="607364498"/>
+ </object>
+ <int key="connectionID">527</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionNatural:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="380031999"/>
+ </object>
+ <int key="connectionID">528</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionLeftToRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="825984362"/>
+ </object>
+ <int key="connectionID">529</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionRightToLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="560145579"/>
+ </object>
+ <int key="connectionID">530</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newWindow:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">533</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">setSharedProcessProcessModel:</string>
+ <reference key="source" ref="976324537"/>
+ <reference key="destination" ref="993856752"/>
+ </object>
+ <int key="connectionID">543</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">setSharedThreadProcessModel:</string>
+ <reference key="source" ref="976324537"/>
+ <reference key="destination" ref="516840223"/>
+ </object>
+ <int key="connectionID">544</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">forceRepaint:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="878165919"/>
+ </object>
+ <int key="connectionID">547</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHideWebView:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="137933275"/>
+ </object>
+ <int key="connectionID">549</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">removeReinsertWebView:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1027125810"/>
+ </object>
+ <int key="connectionID">551</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="302598603"/>
+ <reference ref="448692316"/>
+ <reference ref="816668511"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="835318025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="789758025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="720053764"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1023925487"/>
+ <reference ref="117038363"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="1025936716"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="425164168"/>
+ <reference ref="579971712"/>
+ <reference ref="1010469920"/>
+ </object>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="1023925487"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">80</int>
+ <reference key="object" ref="117038363"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="1025936716"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1065607017"/>
+ </object>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">112</int>
+ <reference key="object" ref="579971712"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="1065607017"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="759406840"/>
+ </object>
+ <reference key="parent" ref="1025936716"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">126</int>
+ <reference key="object" ref="759406840"/>
+ <reference key="parent" ref="1065607017"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">205</int>
+ <reference key="object" ref="789758025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="437104165"/>
+ <reference ref="583158037"/>
+ <reference ref="1058277027"/>
+ <reference ref="212016141"/>
+ <reference ref="296257095"/>
+ <reference ref="29853731"/>
+ <reference ref="860595796"/>
+ <reference ref="1040322652"/>
+ <reference ref="790794224"/>
+ <reference ref="892235320"/>
+ <reference ref="972420730"/>
+ <reference ref="676164635"/>
+ <reference ref="507821607"/>
+ <reference ref="288088188"/>
+ <reference ref="82994268"/>
+ </object>
+ <reference key="parent" ref="952259628"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">202</int>
+ <reference key="object" ref="437104165"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">198</int>
+ <reference key="object" ref="583158037"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">207</int>
+ <reference key="object" ref="1058277027"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">214</int>
+ <reference key="object" ref="212016141"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">199</int>
+ <reference key="object" ref="296257095"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">203</int>
+ <reference key="object" ref="29853731"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">197</int>
+ <reference key="object" ref="860595796"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">206</int>
+ <reference key="object" ref="1040322652"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">215</int>
+ <reference key="object" ref="790794224"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">218</int>
+ <reference key="object" ref="892235320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="963351320"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">216</int>
+ <reference key="object" ref="972420730"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769623530"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">200</int>
+ <reference key="object" ref="769623530"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="948374510"/>
+ <reference ref="96193923"/>
+ <reference ref="679648819"/>
+ <reference ref="967646866"/>
+ <reference ref="859480356"/>
+ <reference ref="795346622"/>
+ </object>
+ <reference key="parent" ref="972420730"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">219</int>
+ <reference key="object" ref="948374510"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">201</int>
+ <reference key="object" ref="96193923"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">204</int>
+ <reference key="object" ref="679648819"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">220</int>
+ <reference key="object" ref="963351320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="270902937"/>
+ <reference ref="88285865"/>
+ <reference ref="159080638"/>
+ <reference ref="326711663"/>
+ <reference ref="447796847"/>
+ </object>
+ <reference key="parent" ref="892235320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">213</int>
+ <reference key="object" ref="270902937"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">210</int>
+ <reference key="object" ref="88285865"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">221</int>
+ <reference key="object" ref="159080638"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">208</int>
+ <reference key="object" ref="326711663"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">209</int>
+ <reference key="object" ref="447796847"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="752062318"/>
+ </object>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="835318025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="299356726"/>
+ <reference ref="625202149"/>
+ <reference ref="575023229"/>
+ <reference ref="1011231497"/>
+ </object>
+ <reference key="parent" ref="713487014"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="299356726"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="625202149"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">239</int>
+ <reference key="object" ref="575023229"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="1011231497"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="466310130"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ </object>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">211</int>
+ <reference key="object" ref="676164635"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="785027613"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">212</int>
+ <reference key="object" ref="785027613"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="680220178"/>
+ <reference ref="731782645"/>
+ </object>
+ <reference key="parent" ref="676164635"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">195</int>
+ <reference key="object" ref="680220178"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">196</int>
+ <reference key="object" ref="731782645"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">346</int>
+ <reference key="object" ref="967646866"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">348</int>
+ <reference key="object" ref="507821607"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="698887838"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">349</int>
+ <reference key="object" ref="698887838"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="605118523"/>
+ <reference ref="197661976"/>
+ <reference ref="708854459"/>
+ <reference ref="65139061"/>
+ <reference ref="19036812"/>
+ <reference ref="672708820"/>
+ <reference ref="537092702"/>
+ </object>
+ <reference key="parent" ref="507821607"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">350</int>
+ <reference key="object" ref="605118523"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">351</int>
+ <reference key="object" ref="197661976"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">354</int>
+ <reference key="object" ref="708854459"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">375</int>
+ <reference key="object" ref="302598603"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="941447902"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">376</int>
+ <reference key="object" ref="941447902"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="792887677"/>
+ <reference ref="215659978"/>
+ </object>
+ <reference key="parent" ref="302598603"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">377</int>
+ <reference key="object" ref="792887677"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="786677654"/>
+ </object>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">388</int>
+ <reference key="object" ref="786677654"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="159677712"/>
+ <reference ref="305399458"/>
+ <reference ref="814362025"/>
+ <reference ref="330926929"/>
+ <reference ref="533507878"/>
+ <reference ref="158063935"/>
+ <reference ref="885547335"/>
+ <reference ref="901062459"/>
+ <reference ref="767671776"/>
+ <reference ref="691570813"/>
+ <reference ref="769124883"/>
+ <reference ref="739652853"/>
+ <reference ref="1012600125"/>
+ <reference ref="214559597"/>
+ <reference ref="596732606"/>
+ <reference ref="393423671"/>
+ </object>
+ <reference key="parent" ref="792887677"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">389</int>
+ <reference key="object" ref="159677712"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">390</int>
+ <reference key="object" ref="305399458"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">391</int>
+ <reference key="object" ref="814362025"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">392</int>
+ <reference key="object" ref="330926929"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">393</int>
+ <reference key="object" ref="533507878"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">394</int>
+ <reference key="object" ref="158063935"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">395</int>
+ <reference key="object" ref="885547335"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">396</int>
+ <reference key="object" ref="901062459"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">397</int>
+ <reference key="object" ref="767671776"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="175441468"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">398</int>
+ <reference key="object" ref="691570813"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1058217995"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">399</int>
+ <reference key="object" ref="769124883"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="18263474"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">400</int>
+ <reference key="object" ref="739652853"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">401</int>
+ <reference key="object" ref="1012600125"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">402</int>
+ <reference key="object" ref="214559597"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">403</int>
+ <reference key="object" ref="596732606"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">404</int>
+ <reference key="object" ref="393423671"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">405</int>
+ <reference key="object" ref="18263474"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="257962622"/>
+ <reference ref="644725453"/>
+ <reference ref="1037576581"/>
+ <reference ref="941806246"/>
+ <reference ref="1045724900"/>
+ </object>
+ <reference key="parent" ref="769124883"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">406</int>
+ <reference key="object" ref="257962622"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">407</int>
+ <reference key="object" ref="644725453"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">408</int>
+ <reference key="object" ref="1037576581"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">409</int>
+ <reference key="object" ref="941806246"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">410</int>
+ <reference key="object" ref="1045724900"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">411</int>
+ <reference key="object" ref="1058217995"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="706297211"/>
+ <reference ref="568384683"/>
+ <reference ref="663508465"/>
+ </object>
+ <reference key="parent" ref="691570813"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">412</int>
+ <reference key="object" ref="706297211"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">413</int>
+ <reference key="object" ref="568384683"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">414</int>
+ <reference key="object" ref="663508465"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">415</int>
+ <reference key="object" ref="175441468"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="252969304"/>
+ <reference ref="766922938"/>
+ <reference ref="677519740"/>
+ <reference ref="238351151"/>
+ </object>
+ <reference key="parent" ref="767671776"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">416</int>
+ <reference key="object" ref="252969304"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">417</int>
+ <reference key="object" ref="766922938"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">418</int>
+ <reference key="object" ref="677519740"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">419</int>
+ <reference key="object" ref="238351151"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">420</int>
+ <reference key="object" ref="755631768"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">450</int>
+ <reference key="object" ref="288088188"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="579392910"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">451</int>
+ <reference key="object" ref="579392910"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1060694897"/>
+ <reference ref="879586729"/>
+ <reference ref="56570060"/>
+ </object>
+ <reference key="parent" ref="288088188"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">452</int>
+ <reference key="object" ref="1060694897"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">453</int>
+ <reference key="object" ref="859480356"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">454</int>
+ <reference key="object" ref="795346622"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">457</int>
+ <reference key="object" ref="65139061"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">459</int>
+ <reference key="object" ref="19036812"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">460</int>
+ <reference key="object" ref="672708820"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">462</int>
+ <reference key="object" ref="537092702"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">465</int>
+ <reference key="object" ref="879586729"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">466</int>
+ <reference key="object" ref="56570060"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">485</int>
+ <reference key="object" ref="82994268"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">490</int>
+ <reference key="object" ref="448692316"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="992780483"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">491</int>
+ <reference key="object" ref="992780483"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="105068016"/>
+ </object>
+ <reference key="parent" ref="448692316"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">492</int>
+ <reference key="object" ref="105068016"/>
+ <reference key="parent" ref="992780483"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">494</int>
+ <reference key="object" ref="976324537"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">496</int>
+ <reference key="object" ref="215659978"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="446991534"/>
+ </object>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">497</int>
+ <reference key="object" ref="446991534"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="875092757"/>
+ <reference ref="630155264"/>
+ <reference ref="945678886"/>
+ <reference ref="512868991"/>
+ <reference ref="163117631"/>
+ <reference ref="31516759"/>
+ <reference ref="908105787"/>
+ <reference ref="644046920"/>
+ <reference ref="231811626"/>
+ <reference ref="883618387"/>
+ </object>
+ <reference key="parent" ref="215659978"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">498</int>
+ <reference key="object" ref="875092757"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">499</int>
+ <reference key="object" ref="630155264"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">500</int>
+ <reference key="object" ref="945678886"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">501</int>
+ <reference key="object" ref="512868991"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">502</int>
+ <reference key="object" ref="163117631"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">503</int>
+ <reference key="object" ref="31516759"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="956096989"/>
+ </object>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">504</int>
+ <reference key="object" ref="908105787"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">505</int>
+ <reference key="object" ref="644046920"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">506</int>
+ <reference key="object" ref="231811626"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">507</int>
+ <reference key="object" ref="883618387"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">508</int>
+ <reference key="object" ref="956096989"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="257099033"/>
+ <reference ref="551969625"/>
+ <reference ref="249532473"/>
+ <reference ref="607364498"/>
+ <reference ref="508151438"/>
+ <reference ref="981751889"/>
+ <reference ref="380031999"/>
+ <reference ref="825984362"/>
+ <reference ref="560145579"/>
+ </object>
+ <reference key="parent" ref="31516759"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">509</int>
+ <reference key="object" ref="257099033"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">510</int>
+ <reference key="object" ref="551969625"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">511</int>
+ <reference key="object" ref="249532473"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">512</int>
+ <reference key="object" ref="607364498"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">513</int>
+ <reference key="object" ref="508151438"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">514</int>
+ <reference key="object" ref="981751889"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">515</int>
+ <reference key="object" ref="380031999"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">516</int>
+ <reference key="object" ref="825984362"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">517</int>
+ <reference key="object" ref="560145579"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">534</int>
+ <reference key="object" ref="816668511"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="865232259"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">535</int>
+ <reference key="object" ref="865232259"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="340689355"/>
+ <reference ref="878165919"/>
+ <reference ref="162978490"/>
+ <reference ref="137933275"/>
+ <reference ref="1027125810"/>
+ </object>
+ <reference key="parent" ref="816668511"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">537</int>
+ <reference key="object" ref="340689355"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="935112943"/>
+ </object>
+ <reference key="parent" ref="865232259"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">538</int>
+ <reference key="object" ref="935112943"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="993856752"/>
+ <reference ref="516840223"/>
+ </object>
+ <reference key="parent" ref="340689355"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">539</int>
+ <reference key="object" ref="993856752"/>
+ <reference key="parent" ref="935112943"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">540</int>
+ <reference key="object" ref="516840223"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="935112943"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">545</int>
+ <reference key="object" ref="878165919"/>
+ <reference key="parent" ref="865232259"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">546</int>
+ <reference key="object" ref="162978490"/>
+ <reference key="parent" ref="865232259"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">548</int>
+ <reference key="object" ref="137933275"/>
+ <reference key="parent" ref="865232259"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">550</int>
+ <reference key="object" ref="1027125810"/>
+ <reference key="parent" ref="865232259"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-3.IBPluginDependency</string>
+ <string>112.IBPluginDependency</string>
+ <string>112.ImportedFromIB2</string>
+ <string>124.IBPluginDependency</string>
+ <string>124.ImportedFromIB2</string>
+ <string>125.IBPluginDependency</string>
+ <string>125.ImportedFromIB2</string>
+ <string>125.editorWindowContentRectSynchronizationRect</string>
+ <string>126.IBPluginDependency</string>
+ <string>126.ImportedFromIB2</string>
+ <string>129.IBPluginDependency</string>
+ <string>129.ImportedFromIB2</string>
+ <string>130.IBPluginDependency</string>
+ <string>130.ImportedFromIB2</string>
+ <string>130.editorWindowContentRectSynchronizationRect</string>
+ <string>131.IBPluginDependency</string>
+ <string>131.ImportedFromIB2</string>
+ <string>134.IBPluginDependency</string>
+ <string>134.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>143.IBPluginDependency</string>
+ <string>143.ImportedFromIB2</string>
+ <string>144.IBPluginDependency</string>
+ <string>144.ImportedFromIB2</string>
+ <string>145.IBPluginDependency</string>
+ <string>145.ImportedFromIB2</string>
+ <string>149.IBPluginDependency</string>
+ <string>149.ImportedFromIB2</string>
+ <string>150.IBPluginDependency</string>
+ <string>150.ImportedFromIB2</string>
+ <string>19.IBPluginDependency</string>
+ <string>19.ImportedFromIB2</string>
+ <string>195.IBPluginDependency</string>
+ <string>195.ImportedFromIB2</string>
+ <string>196.IBPluginDependency</string>
+ <string>196.ImportedFromIB2</string>
+ <string>197.IBPluginDependency</string>
+ <string>197.ImportedFromIB2</string>
+ <string>198.IBPluginDependency</string>
+ <string>198.ImportedFromIB2</string>
+ <string>199.IBPluginDependency</string>
+ <string>199.ImportedFromIB2</string>
+ <string>200.IBEditorWindowLastContentRect</string>
+ <string>200.IBPluginDependency</string>
+ <string>200.ImportedFromIB2</string>
+ <string>200.editorWindowContentRectSynchronizationRect</string>
+ <string>201.IBPluginDependency</string>
+ <string>201.ImportedFromIB2</string>
+ <string>202.IBPluginDependency</string>
+ <string>202.ImportedFromIB2</string>
+ <string>203.IBPluginDependency</string>
+ <string>203.ImportedFromIB2</string>
+ <string>204.IBPluginDependency</string>
+ <string>204.ImportedFromIB2</string>
+ <string>205.IBEditorWindowLastContentRect</string>
+ <string>205.IBPluginDependency</string>
+ <string>205.ImportedFromIB2</string>
+ <string>205.editorWindowContentRectSynchronizationRect</string>
+ <string>206.IBPluginDependency</string>
+ <string>206.ImportedFromIB2</string>
+ <string>207.IBPluginDependency</string>
+ <string>207.ImportedFromIB2</string>
+ <string>208.IBPluginDependency</string>
+ <string>208.ImportedFromIB2</string>
+ <string>209.IBPluginDependency</string>
+ <string>209.ImportedFromIB2</string>
+ <string>210.IBPluginDependency</string>
+ <string>210.ImportedFromIB2</string>
+ <string>211.IBPluginDependency</string>
+ <string>211.ImportedFromIB2</string>
+ <string>212.IBPluginDependency</string>
+ <string>212.ImportedFromIB2</string>
+ <string>212.editorWindowContentRectSynchronizationRect</string>
+ <string>213.IBPluginDependency</string>
+ <string>213.ImportedFromIB2</string>
+ <string>214.IBPluginDependency</string>
+ <string>214.ImportedFromIB2</string>
+ <string>215.IBPluginDependency</string>
+ <string>215.ImportedFromIB2</string>
+ <string>216.IBPluginDependency</string>
+ <string>216.ImportedFromIB2</string>
+ <string>217.IBPluginDependency</string>
+ <string>217.ImportedFromIB2</string>
+ <string>218.IBPluginDependency</string>
+ <string>218.ImportedFromIB2</string>
+ <string>219.IBPluginDependency</string>
+ <string>219.ImportedFromIB2</string>
+ <string>220.IBEditorWindowLastContentRect</string>
+ <string>220.IBPluginDependency</string>
+ <string>220.ImportedFromIB2</string>
+ <string>220.editorWindowContentRectSynchronizationRect</string>
+ <string>221.IBPluginDependency</string>
+ <string>221.ImportedFromIB2</string>
+ <string>23.IBPluginDependency</string>
+ <string>23.ImportedFromIB2</string>
+ <string>236.IBPluginDependency</string>
+ <string>236.ImportedFromIB2</string>
+ <string>239.IBPluginDependency</string>
+ <string>239.ImportedFromIB2</string>
+ <string>24.IBEditorWindowLastContentRect</string>
+ <string>24.IBPluginDependency</string>
+ <string>24.ImportedFromIB2</string>
+ <string>24.editorWindowContentRectSynchronizationRect</string>
+ <string>29.IBEditorWindowLastContentRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>296.IBEditorWindowLastContentRect</string>
+ <string>296.IBPluginDependency</string>
+ <string>296.editorWindowContentRectSynchronizationRect</string>
+ <string>297.IBPluginDependency</string>
+ <string>298.IBPluginDependency</string>
+ <string>346.IBPluginDependency</string>
+ <string>346.ImportedFromIB2</string>
+ <string>348.IBPluginDependency</string>
+ <string>348.ImportedFromIB2</string>
+ <string>349.IBEditorWindowLastContentRect</string>
+ <string>349.IBPluginDependency</string>
+ <string>349.ImportedFromIB2</string>
+ <string>349.editorWindowContentRectSynchronizationRect</string>
+ <string>350.IBPluginDependency</string>
+ <string>350.ImportedFromIB2</string>
+ <string>351.IBPluginDependency</string>
+ <string>351.ImportedFromIB2</string>
+ <string>354.IBPluginDependency</string>
+ <string>354.ImportedFromIB2</string>
+ <string>375.IBPluginDependency</string>
+ <string>376.IBEditorWindowLastContentRect</string>
+ <string>376.IBPluginDependency</string>
+ <string>377.IBPluginDependency</string>
+ <string>388.IBEditorWindowLastContentRect</string>
+ <string>388.IBPluginDependency</string>
+ <string>389.IBPluginDependency</string>
+ <string>390.IBPluginDependency</string>
+ <string>391.IBPluginDependency</string>
+ <string>392.IBPluginDependency</string>
+ <string>393.IBPluginDependency</string>
+ <string>394.IBPluginDependency</string>
+ <string>395.IBPluginDependency</string>
+ <string>396.IBPluginDependency</string>
+ <string>397.IBPluginDependency</string>
+ <string>398.IBPluginDependency</string>
+ <string>399.IBPluginDependency</string>
+ <string>400.IBPluginDependency</string>
+ <string>401.IBPluginDependency</string>
+ <string>402.IBPluginDependency</string>
+ <string>403.IBPluginDependency</string>
+ <string>404.IBPluginDependency</string>
+ <string>405.IBPluginDependency</string>
+ <string>406.IBPluginDependency</string>
+ <string>407.IBPluginDependency</string>
+ <string>408.IBPluginDependency</string>
+ <string>409.IBPluginDependency</string>
+ <string>410.IBPluginDependency</string>
+ <string>411.IBPluginDependency</string>
+ <string>412.IBPluginDependency</string>
+ <string>413.IBPluginDependency</string>
+ <string>414.IBPluginDependency</string>
+ <string>415.IBPluginDependency</string>
+ <string>416.IBPluginDependency</string>
+ <string>417.IBPluginDependency</string>
+ <string>418.IBPluginDependency</string>
+ <string>419.IBPluginDependency</string>
+ <string>450.IBPluginDependency</string>
+ <string>451.IBEditorWindowLastContentRect</string>
+ <string>451.IBPluginDependency</string>
+ <string>452.IBPluginDependency</string>
+ <string>453.IBPluginDependency</string>
+ <string>454.IBPluginDependency</string>
+ <string>457.IBPluginDependency</string>
+ <string>459.IBPluginDependency</string>
+ <string>460.IBPluginDependency</string>
+ <string>462.IBPluginDependency</string>
+ <string>465.IBPluginDependency</string>
+ <string>466.IBPluginDependency</string>
+ <string>485.IBPluginDependency</string>
+ <string>490.IBPluginDependency</string>
+ <string>491.IBEditorWindowLastContentRect</string>
+ <string>491.IBPluginDependency</string>
+ <string>492.IBPluginDependency</string>
+ <string>496.IBPluginDependency</string>
+ <string>497.IBEditorWindowLastContentRect</string>
+ <string>497.IBPluginDependency</string>
+ <string>498.IBPluginDependency</string>
+ <string>499.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>5.ImportedFromIB2</string>
+ <string>500.IBPluginDependency</string>
+ <string>501.IBPluginDependency</string>
+ <string>502.IBPluginDependency</string>
+ <string>503.IBPluginDependency</string>
+ <string>504.IBPluginDependency</string>
+ <string>505.IBPluginDependency</string>
+ <string>506.IBPluginDependency</string>
+ <string>507.IBPluginDependency</string>
+ <string>508.IBEditorWindowLastContentRect</string>
+ <string>508.IBPluginDependency</string>
+ <string>509.IBPluginDependency</string>
+ <string>510.IBPluginDependency</string>
+ <string>511.IBPluginDependency</string>
+ <string>512.IBPluginDependency</string>
+ <string>513.IBPluginDependency</string>
+ <string>514.IBPluginDependency</string>
+ <string>515.IBPluginDependency</string>
+ <string>516.IBPluginDependency</string>
+ <string>517.IBPluginDependency</string>
+ <string>534.IBPluginDependency</string>
+ <string>535.IBEditorWindowLastContentRect</string>
+ <string>535.IBPluginDependency</string>
+ <string>537.IBPluginDependency</string>
+ <string>538.IBEditorWindowLastContentRect</string>
+ <string>538.IBPluginDependency</string>
+ <string>539.IBPluginDependency</string>
+ <string>540.IBPluginDependency</string>
+ <string>545.IBPluginDependency</string>
+ <string>546.IBPluginDependency</string>
+ <string>548.IBPluginDependency</string>
+ <string>550.IBPluginDependency</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBEditorWindowLastContentRect</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>58.IBPluginDependency</string>
+ <string>58.ImportedFromIB2</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.ImportedFromIB2</string>
+ <string>73.IBPluginDependency</string>
+ <string>73.ImportedFromIB2</string>
+ <string>74.IBPluginDependency</string>
+ <string>74.ImportedFromIB2</string>
+ <string>75.IBPluginDependency</string>
+ <string>75.ImportedFromIB2</string>
+ <string>77.IBPluginDependency</string>
+ <string>77.ImportedFromIB2</string>
+ <string>78.IBPluginDependency</string>
+ <string>78.ImportedFromIB2</string>
+ <string>79.IBPluginDependency</string>
+ <string>79.ImportedFromIB2</string>
+ <string>80.IBPluginDependency</string>
+ <string>80.ImportedFromIB2</string>
+ <string>81.IBEditorWindowLastContentRect</string>
+ <string>81.IBPluginDependency</string>
+ <string>81.ImportedFromIB2</string>
+ <string>81.editorWindowContentRectSynchronizationRect</string>
+ <string>82.IBPluginDependency</string>
+ <string>82.ImportedFromIB2</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ <string>92.IBPluginDependency</string>
+ <string>92.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{522, 812}, {146, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{436, 809}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{753, 187}, {275, 113}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{608, 612}, {275, 83}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{546, 553}, {254, 283}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{187, 434}, {243, 243}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{608, 612}, {167, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{753, 217}, {238, 103}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{608, 612}, {241, 103}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{639, 872}, {194, 73}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{525, 802}, {197, 73}}</string>
+ <string>{{317, 945}, {517, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{74, 862}</string>
+ <string>{{6, 978}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{656, 793}, {231, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{475, 832}, {234, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{746, 287}, {220, 133}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{608, 612}, {215, 63}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{527, 902}, {83, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{523, 2}, {178, 283}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{753, 197}, {170, 63}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{714, 922}, {193, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{674, 260}, {204, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{878, 180}, {164, 173}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{759, 852}, {211, 93}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{944, 872}, {154, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{286, 129}, {275, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{504, 633}, {196, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>{{145, 474}, {199, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">551</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserAppDelegate</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>setSharedProcessProcessModel:</string>
+ <string>setSharedThreadProcessModel:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>setSharedProcessProcessModel:</string>
+ <string>setSharedThreadProcessModel:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">setSharedProcessProcessModel:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">setSharedThreadProcessModel:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">mac/AppDelegate.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserAppDelegate</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">window</string>
+ <string key="NS.object.0">NSWindow</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">window</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">window</string>
+ <string key="candidateClassName">NSWindow</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">BrowserWindowController</string>
+ <string key="superclassName">NSWindowController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>fetch:</string>
+ <string>forceRepaint:</string>
+ <string>reload:</string>
+ <string>removeReinsertWebView:</string>
+ <string>showHideWebView:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>fetch:</string>
+ <string>forceRepaint:</string>
+ <string>reload:</string>
+ <string>removeReinsertWebView:</string>
+ <string>showHideWebView:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">fetch:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">forceRepaint:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">reload:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">removeReinsertWebView:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">showHideWebView:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>containerView</string>
+ <string>progressIndicator</string>
+ <string>reloadButton</string>
+ <string>urlText</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSView</string>
+ <string>NSProgressIndicator</string>
+ <string>NSButton</string>
+ <string>NSTextField</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>containerView</string>
+ <string>progressIndicator</string>
+ <string>reloadButton</string>
+ <string>urlText</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">containerView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">progressIndicator</string>
+ <string key="candidateClassName">NSProgressIndicator</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">reloadButton</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">urlText</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">mac/BrowserWindowController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">FirstResponder</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>forceRepaint:</string>
+ <string>newWindow:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>forceRepaint:</string>
+ <string>newWindow:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">forceRepaint:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">newWindow:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="822405504">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSApplication.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="850738725">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSApplicationScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="624831158">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSColorPanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSHelpManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSPageLayout.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSUserInterfaceItemSearching.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSBrowser</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSBrowser.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSButton</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSButton.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSControl</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="310914472">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSControl.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSDocument</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>printDocument:</string>
+ <string>revertDocumentToSaved:</string>
+ <string>runPageLayout:</string>
+ <string>saveDocument:</string>
+ <string>saveDocumentAs:</string>
+ <string>saveDocumentTo:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>printDocument:</string>
+ <string>revertDocumentToSaved:</string>
+ <string>runPageLayout:</string>
+ <string>saveDocument:</string>
+ <string>saveDocumentAs:</string>
+ <string>saveDocumentTo:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">printDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">revertDocumentToSaved:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">runPageLayout:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">saveDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">saveDocumentAs:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">saveDocumentTo:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDocument.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSDocument</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDocumentScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSDocumentController</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>clearRecentDocuments:</string>
+ <string>newDocument:</string>
+ <string>openDocument:</string>
+ <string>saveAllDocuments:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>clearRecentDocuments:</string>
+ <string>newDocument:</string>
+ <string>openDocument:</string>
+ <string>saveAllDocuments:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBActionInfo">
+ <string key="name">clearRecentDocuments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">newDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">openDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo">
+ <string key="name">saveAllDocuments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDocumentController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSFontManager</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="946436764">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSFontManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSFormatter</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMatrix</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMatrix.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenu</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="1056362899">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMenu.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenuItem</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="472958451">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMovieView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSMovieView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSAccessibility.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="822405504"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="850738725"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="624831158"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="310914472"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDictionaryController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDragging.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="946436764"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSFontPanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSKeyValueBinding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <reference key="sourceIdentifier" ref="1056362899"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSNibLoading.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSOutlineView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSPasteboard.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSSavePanel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="809545482">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSToolbarItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="260078765">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSProgressIndicator</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSProgressIndicator.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSInterfaceStyle.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSResponder</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTableView</string>
+ <string key="superclassName">NSControl</string>
+ <reference key="sourceIdentifier" ref="809545482"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSText</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSText.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextField</string>
+ <string key="superclassName">NSControl</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTextField.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTextView</string>
+ <string key="superclassName">NSText</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSTextView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSClipView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <reference key="sourceIdentifier" ref="472958451"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSRulerView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <string key="superclassName">NSResponder</string>
+ <reference key="sourceIdentifier" ref="260078765"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSDrawer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindow.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindowScripting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindowController</string>
+ <string key="superclassName">NSResponder</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">showWindow:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">showWindow:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">showWindow:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <integer value="1060" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../MiniBrowser.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSMenuCheckmark</string>
+ <string>NSMenuMixedState</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{9, 8}</string>
+ <string>{7, 2}</string>
+ </object>
+ </object>
+ </data>
+</archive>
diff --git a/WebKitTools/MiniBrowser/mac/MiniBrowser-Info.plist b/WebKitTools/MiniBrowser/mac/MiniBrowser-Info.plist
new file mode 100644
index 0000000..7564a7e
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/MiniBrowser-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>MiniBrowser</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/WebKitTools/MiniBrowser/mac/MiniBrowser_Prefix.pch b/WebKitTools/MiniBrowser/mac/MiniBrowser_Prefix.pch
new file mode 100644
index 0000000..bd7fc4b
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/MiniBrowser_Prefix.pch
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
diff --git a/WebKitTools/MiniBrowser/mac/main.m b/WebKitTools/MiniBrowser/mac/main.m
new file mode 100644
index 0000000..7f56737
--- /dev/null
+++ b/WebKitTools/MiniBrowser/mac/main.m
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
diff --git a/WebKitTools/MiniBrowser/win/BrowserView.cpp b/WebKitTools/MiniBrowser/win/BrowserView.cpp
new file mode 100644
index 0000000..95385ca
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/BrowserView.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 "StdAfx.h"
+#include "BrowserView.h"
+
+#include "BrowserWindow.h"
+#include <WebKit2/WKURLCF.h>
+
+static const unsigned short HIGH_BIT_MASK_SHORT = 0x8000;
+
+BrowserView::BrowserView()
+ : m_webView(0)
+{
+}
+
+// UI Client Callbacks
+
+static WKPageRef createNewPage(WKPageRef page, const void* clientInfo)
+{
+ BrowserWindow* browserWindow = BrowserWindow::create();
+ browserWindow->createWindow(0, 0, 800, 600);
+
+ return WKViewGetPage(browserWindow->view().webView());
+}
+
+static void showPage(WKPageRef page, const void *clientInfo)
+{
+ static_cast<BrowserWindow*>(const_cast<void*>(clientInfo))->showWindow();
+}
+
+static void closePage(WKPageRef page, const void *clientInfo)
+{
+}
+
+static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, const void* clientInfo)
+{
+}
+
+
+void BrowserView::create(RECT webViewRect, BrowserWindow* parentWindow)
+{
+ assert(!m_webView);
+
+ bool isShiftKeyDown = ::GetKeyState(VK_SHIFT) & HIGH_BIT_MASK_SHORT;
+
+ //WKContextRef context = WKContextCreateWithProcessModel(isShiftKeyDown ? kWKProcessModelSecondaryThread : kWKProcessModelSecondaryProcess);
+ WKContextRef context = WKContextCreateWithProcessModel(kWKProcessModelSecondaryThread);
+ WKPageNamespaceRef pageNamespace = WKPageNamespaceCreate(context);
+
+ m_webView = WKViewCreate(webViewRect, pageNamespace, parentWindow->window());
+
+ WKPageUIClient uiClient = {
+ 0, /* version */
+ parentWindow, /* clientInfo */
+ createNewPage,
+ showPage,
+ closePage,
+ runJavaScriptAlert
+ };
+ WKPageSetPageUIClient(WKViewGetPage(m_webView), &uiClient);
+}
+
+void BrowserView::setFrame(RECT rect)
+{
+ HWND webViewWindow = WKViewGetWindow(m_webView);
+ ::SetWindowPos(webViewWindow, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
+}
+
+void BrowserView::goToURL(const std::wstring& urlString)
+{
+ CFStringRef string = CFStringCreateWithCharacters(0, (const UniChar*)urlString.data(), urlString.size());
+ CFURLRef cfURL = CFURLCreateWithString(0, string, 0);
+ CFRelease(string);
+
+ WKURLRef url = WKURLCreateWithCFURL(cfURL);
+ CFRelease(cfURL);
+
+ WKPageRef page = WKViewGetPage(m_webView);
+ WKPageLoadURL(page, url);
+ WKURLRelease(url);
+}
diff --git a/WebKitTools/MiniBrowser/win/BrowserView.h b/WebKitTools/MiniBrowser/win/BrowserView.h
new file mode 100644
index 0000000..08c5e4a
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/BrowserView.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 BrowserView_h
+#define BrowserView_h
+
+#include <WebKit2/WebKit2.h>
+#include <string>
+
+class BrowserWindow;
+
+class BrowserView {
+public:
+ BrowserView();
+
+ void goToURL(const std::wstring& url);
+
+ void create(RECT, BrowserWindow* parentWindow);
+ void setFrame(RECT);
+
+ WKViewRef webView() const { return m_webView; }
+
+private:
+ WKViewRef m_webView;
+};
+
+#endif // BrowserView_h
diff --git a/WebKitTools/MiniBrowser/win/BrowserWindow.cpp b/WebKitTools/MiniBrowser/win/BrowserWindow.cpp
new file mode 100644
index 0000000..afe7e42
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/BrowserWindow.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 "StdAfx.h"
+#include "BrowserWindow.h"
+#include "MiniBrowser.h"
+#include "Resource.h"
+
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <vector>
+#include <wininet.h>
+
+using namespace std;
+
+BrowserWindow::BrowserWindow()
+ : m_window(0)
+ , m_rebarWindow(0)
+ , m_comboBoxWindow(0)
+{
+}
+
+LRESULT CALLBACK BrowserWindow::BrowserWindowWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LONG_PTR longPtr = ::GetWindowLongPtr(window, 0);
+
+ if (BrowserWindow* browserView = reinterpret_cast<BrowserWindow*>(longPtr))
+ return browserView->wndProc(window, message, wParam, lParam);
+
+ if (message == WM_CREATE) {
+ LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
+ BrowserWindow* browserWindow = static_cast<BrowserWindow*>(createStruct->lpCreateParams);
+ browserWindow->m_window = window;
+
+ ::SetWindowLongPtr(window, 0, (LONG_PTR)browserWindow);
+
+ browserWindow->onCreate(createStruct);
+ return 0;
+ }
+
+ return ::DefWindowProc(window, message, wParam, lParam);
+}
+
+LRESULT BrowserWindow::wndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = 0;
+ bool handled = true;
+
+ switch (message) {
+ case WM_COMMAND:
+ lResult = onCommand(LOWORD(wParam), handled);
+ break;
+
+ case WM_SIZE:
+ onSize(LOWORD(lParam), HIWORD(lParam));
+ break;
+
+ case WM_DESTROY:
+ onDestroy();
+ break;
+
+ case WM_NCDESTROY:
+ onNCDestroy();
+ break;
+
+ default:
+ handled = false;
+ }
+
+ if (!handled)
+ lResult = ::DefWindowProc(window, message, wParam, lParam);
+
+ return lResult;
+}
+
+void BrowserWindow::createWindow(int x, int y, int width, int height)
+{
+ assert(!m_window);
+
+ // Register the class.
+ WNDCLASSEX windowClass = { 0 };
+ windowClass.cbSize = sizeof(windowClass);
+ windowClass.style = 0;
+ windowClass.lpfnWndProc = BrowserWindowWndProc;
+ windowClass.cbClsExtra = 0;
+ windowClass.cbWndExtra = sizeof(this);
+ windowClass.hInstance = MiniBrowser::shared().instance();
+ windowClass.hIcon = 0;
+ windowClass.hCursor = ::LoadCursor(0, IDC_ARROW);
+ windowClass.hbrBackground = 0;
+ windowClass.lpszMenuName = MAKEINTRESOURCE(IDR_MAINFRAME);
+ windowClass.lpszClassName = L"MiniBrowser";
+ windowClass.hIconSm = 0;
+
+ ::RegisterClassEx(&windowClass);
+
+ ::CreateWindowW(L"MiniBrowser", L"MiniBrowser", WS_OVERLAPPEDWINDOW, x, y, width, height, 0, 0, MiniBrowser::shared().instance(), this);
+}
+
+void BrowserWindow::showWindow()
+{
+ assert(m_window);
+
+ ::ShowWindow(m_window, SW_SHOWNORMAL);
+}
+
+void BrowserWindow::goToURL(const std::wstring& url)
+{
+ m_browserView.goToURL(url);
+}
+
+void BrowserWindow::onCreate(LPCREATESTRUCT createStruct)
+{
+ // Register our window.
+ MiniBrowser::shared().registerWindow(this);
+
+ // Create the rebar control.
+ m_rebarWindow = ::CreateWindowEx(0, REBARCLASSNAME, 0, WS_VISIBLE | WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | RBS_VARHEIGHT | RBS_BANDBORDERS,
+ 0, 0, 0, 0, m_window, 0, createStruct->hInstance, 0);
+ REBARINFO rebarInfo = { 0 };
+ rebarInfo.cbSize = sizeof(rebarInfo);
+ rebarInfo.fMask = 0;
+ ::SendMessage(m_rebarWindow, RB_SETBARINFO, 0, (LPARAM)&rebarInfo);
+
+ // Create the combo box control.
+ m_comboBoxWindow = ::CreateWindowEx(0, L"combobox", 0, WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CBS_AUTOHSCROLL | CBS_DROPDOWN,
+ 0, 0, 0, 0, m_rebarWindow, 0, createStruct->hInstance, 0);
+ SendMessage(m_comboBoxWindow, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0));
+
+ REBARBANDINFO bandInfo;
+ bandInfo.cbSize = sizeof(bandInfo);
+ bandInfo.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE;
+ bandInfo.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS;
+ bandInfo.lpText = L"Address";
+ bandInfo.hwndChild = m_comboBoxWindow;
+
+ RECT comboBoxRect;
+ ::GetWindowRect(m_comboBoxWindow, &comboBoxRect);
+ bandInfo.cx = 100;
+ bandInfo.cxMinChild = comboBoxRect.right - comboBoxRect.left;
+ bandInfo.cyMinChild = comboBoxRect.bottom - comboBoxRect.top;
+
+ // Add the band to the rebar.
+ int result = ::SendMessage(m_rebarWindow, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&bandInfo);
+
+ // Create the browser view.
+ RECT webViewRect = { 0, 0, 0, 0};
+ m_browserView.create(webViewRect, this);
+}
+
+void BrowserWindow::onDestroy()
+{
+ MiniBrowser::shared().unregisterWindow(this);
+
+ // FIXME: Should we close the browser view here?
+}
+
+void BrowserWindow::onNCDestroy()
+{
+ delete this;
+}
+
+void BrowserWindow::onSize(int width, int height)
+{
+ RECT rebarRect;
+ ::GetClientRect(m_rebarWindow, &rebarRect);
+
+ // Resize the rebar.
+ ::MoveWindow(m_rebarWindow, 0, 0, width, rebarRect.bottom - rebarRect.top, true);
+
+ RECT webViewRect;
+ webViewRect.top = rebarRect.bottom;
+ webViewRect.left = 0;
+ webViewRect.right = width;
+ webViewRect.bottom = height;
+ m_browserView.setFrame(webViewRect);
+}
+
+LRESULT BrowserWindow::onCommand(int commandID, bool& handled)
+{
+ switch (commandID) {
+ case ID_FILE_NEW_WINDOW:
+ MiniBrowser::shared().createNewWindow();
+ break;
+ case ID_FILE_CLOSE:
+ ::PostMessage(m_window, WM_CLOSE, 0, 0);
+ break;
+ default:
+ handled = false;
+ }
+
+ return 0;
+}
+
+bool BrowserWindow::handleMessage(const MSG* message)
+{
+ if (message->hwnd != m_comboBoxWindow && !::IsChild(m_comboBoxWindow, message->hwnd))
+ return false;
+
+ // Look for a WM_KEYDOWN message.
+ if (message->message != WM_KEYDOWN)
+ return false;
+
+ // Look for the VK_RETURN key.
+ if (message->wParam != VK_RETURN)
+ return false;
+
+ std::vector<WCHAR> buffer;
+ int textLength = ::GetWindowTextLength(m_comboBoxWindow);
+
+ buffer.resize(textLength + 1);
+ ::GetWindowText(m_comboBoxWindow, &buffer[0], buffer.size());
+
+ std::wstring url(&buffer[0], buffer.size() - 1);
+
+ if (url.find(L"http://"))
+ url = L"http://" + url;
+
+ m_browserView.goToURL(url);
+
+ // We handled this message.
+ return true;
+}
diff --git a/WebKitTools/MiniBrowser/win/BrowserWindow.h b/WebKitTools/MiniBrowser/win/BrowserWindow.h
new file mode 100644
index 0000000..1cf7350
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/BrowserWindow.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 BrowserWindow_h
+#define BrowserWindow_h
+
+#include "BrowserView.h"
+#include <string>
+
+class BrowserWindow {
+public:
+ static BrowserWindow* create()
+ {
+ return new BrowserWindow;
+ }
+
+ void createWindow(int x, int y, int width, int height);
+ void showWindow();
+
+ void goToURL(const std::wstring& url);
+
+ bool handleMessage(const MSG*);
+
+ const BrowserView& view() const { return m_browserView; }
+ HWND window() const { return m_window; }
+
+private:
+ BrowserWindow();
+
+ static LRESULT CALLBACK BrowserWindowWndProc(HWND, UINT, WPARAM, LPARAM);
+
+ // Message handlers.
+ LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
+ void onCreate(LPCREATESTRUCT);
+ void onDestroy();
+ void onNCDestroy();
+
+ void onSize(int width, int height);
+ LRESULT onCommand(int commandID, bool& handled);
+
+ HWND m_window;
+
+ HWND m_rebarWindow;
+ HWND m_comboBoxWindow;
+ BrowserView m_browserView;
+};
+
+#endif // BrowserWindow_h
diff --git a/WebKitTools/MiniBrowser/win/MiniBrowser.cpp b/WebKitTools/MiniBrowser/win/MiniBrowser.cpp
new file mode 100644
index 0000000..7e3d488
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/MiniBrowser.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 "stdafx.h"
+
+#include "BrowserWindow.h"
+#include "MiniBrowser.h"
+#include <assert.h>
+
+MiniBrowser::MiniBrowser()
+ : m_instance(0)
+{
+}
+
+MiniBrowser& MiniBrowser::shared()
+{
+ static MiniBrowser miniBrowser;
+
+ return miniBrowser;
+}
+
+void MiniBrowser::initialize(HINSTANCE instance)
+{
+ assert(!m_instance);
+
+ m_instance = instance;
+}
+
+void MiniBrowser::createNewWindow()
+{
+ static const wchar_t* kDefaultURLString = L"http://webkit.org/";
+
+ BrowserWindow* browserWindow = BrowserWindow::create();
+ browserWindow->createWindow(0, 0, 800, 600);
+ browserWindow->showWindow();
+ browserWindow->goToURL(kDefaultURLString);
+}
+
+void MiniBrowser::registerWindow(BrowserWindow* window)
+{
+ m_browserWindows.insert(window);
+}
+
+void MiniBrowser::unregisterWindow(BrowserWindow* window)
+{
+ m_browserWindows.erase(window);
+
+ if (m_browserWindows.empty())
+ ::PostQuitMessage(0);
+}
+
+bool MiniBrowser::handleMessage(const MSG* message)
+{
+ for (std::set<BrowserWindow*>::const_iterator it = m_browserWindows.begin(), end = m_browserWindows.end(); it != end; ++it) {
+ BrowserWindow* browserWindow = *it;
+
+ if (browserWindow->handleMessage(message))
+ return true;
+ }
+
+ return false;
+}
diff --git a/WebKitTools/MiniBrowser/win/MiniBrowser.h b/WebKitTools/MiniBrowser/win/MiniBrowser.h
new file mode 100644
index 0000000..c7b5177
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/MiniBrowser.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 MiniBrowser_h
+#define MiniBrowser_h
+
+#include <set>
+
+class BrowserWindow;
+
+class MiniBrowser {
+public:
+ static MiniBrowser& shared();
+
+ void initialize(HINSTANCE);
+
+ void createNewWindow();
+
+ void registerWindow(BrowserWindow*);
+ void unregisterWindow(BrowserWindow*);
+
+ bool handleMessage(const MSG*);
+
+ HINSTANCE instance() const { return m_instance; }
+
+private:
+ MiniBrowser();
+
+ HINSTANCE m_instance;
+ std::set<BrowserWindow*> m_browserWindows;
+};
+
+#endif // MiniBrowser_h
diff --git a/WebKitTools/MiniBrowser/win/MiniBrowser.rc b/WebKitTools/MiniBrowser/win/MiniBrowser.rc
new file mode 100644
index 0000000..556b0f6
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/MiniBrowser.rc
@@ -0,0 +1,30 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+#include "winresrc.h"
+
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+IDR_MAINFRAME MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&New Window\tCtrl+N", ID_FILE_NEW_WINDOW
+ MENUITEM SEPARATOR
+ MENUITEM "&Close", ID_FILE_CLOSE
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_MAINFRAME_ACCEL ACCELERATORS
+BEGIN
+ "N", ID_FILE_NEW_WINDOW, VIRTKEY, CONTROL, NOINVERT
+END
diff --git a/WebKitTools/MiniBrowser/win/main.cpp b/WebKitTools/MiniBrowser/win/main.cpp
new file mode 100644
index 0000000..0c125ba
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/main.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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 "stdafx.h"
+
+#include "BrowserWindow.h"
+#include "MiniBrowser.h"
+#include <string>
+
+#if defined _M_IX86
+#define PROCESSORARCHITECTURE "x86"
+#elif defined _M_IA64
+#define PROCESSORARCHITECTURE "ia64"
+#elif defined _M_X64
+#define PROCESSORARCHITECTURE "amd64"
+#else
+#define PROCESSORARCHITECTURE "*"
+#endif
+
+#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='" PROCESSORARCHITECTURE "' publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrCmdLine, int nCmdShow)
+{
+ MiniBrowser::shared().initialize(hInstance);
+
+ // Create and show our initial window.
+ MiniBrowser::shared().createNewWindow();
+
+ MSG message;
+ while (BOOL result = ::GetMessage(&message, 0, 0, 0)) {
+ if (result == -1)
+ break;
+ ::TranslateMessage(&message);
+
+ if (!MiniBrowser::shared().handleMessage(&message))
+ ::DispatchMessage(&message);
+ }
+
+ return 0;
+}
diff --git a/WebKitTools/MiniBrowser/win/resource.h b/WebKitTools/MiniBrowser/win/resource.h
new file mode 100644
index 0000000..ce07800
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/resource.h
@@ -0,0 +1,22 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by MiniBrowser.rc
+//
+
+#define ID_FILE_NEW_WINDOW 32770
+#define ID_FILE_OPEN 32771
+#define ID_FILE_CLOSE 32772
+
+#define IDR_MAINFRAME 128
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 132
+#define _APS_NEXT_COMMAND_VALUE 32775
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/WebKitTools/MiniBrowser/win/stdafx.cpp b/WebKitTools/MiniBrowser/win/stdafx.cpp
new file mode 100644
index 0000000..c664e32
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/stdafx.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// MiniBrowser.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/WebKitTools/MiniBrowser/win/stdafx.h b/WebKitTools/MiniBrowser/win/stdafx.h
new file mode 100644
index 0000000..c5a35f9
--- /dev/null
+++ b/WebKitTools/MiniBrowser/win/stdafx.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 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 INC. 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 INC. 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.
+ */
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+
+#include <tchar.h>
+#include <windows.h>
diff --git a/WebKitTools/QtLauncher/QtLauncher.pro b/WebKitTools/QtLauncher/QtLauncher.pro
index 7dcc8e4..0873bda 100644
--- a/WebKitTools/QtLauncher/QtLauncher.pro
+++ b/WebKitTools/QtLauncher/QtLauncher.pro
@@ -20,17 +20,37 @@ HEADERS += \
CONFIG -= app_bundle
CONFIG += uitools
-DESTDIR = ../../bin
+isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../..
include(../../WebKit.pri)
-INCLUDEPATH += ../../JavaScriptCore
+DESTDIR = $$OUTPUT_DIR/bin
QT += network
macx:QT+=xml
-QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR
+
+linux-* {
+ # From Creator's src/rpath.pri:
+ # Do the rpath by hand since it's not possible to use ORIGIN in QMAKE_RPATHDIR
+ # this expands to $ORIGIN (after qmake and make), it does NOT read a qmake var.
+ QMAKE_RPATHDIR = \$\$ORIGIN/../lib $$QMAKE_RPATHDIR
+ MY_RPATH = $$join(QMAKE_RPATHDIR, ":")
+
+ QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${MY_RPATH}\'
+ QMAKE_RPATHDIR =
+} else {
+ QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR
+}
symbian {
TARGET.UID3 = 0xA000E543
TARGET.CAPABILITY = ReadUserData WriteUserData NetworkServices
}
+
+contains(QT_CONFIG, opengl) {
+ QT += opengl
+ DEFINES += QT_CONFIGURED_WITH_OPENGL
+}
+
+RESOURCES += \
+ QtLauncher.qrc
diff --git a/WebKitTools/QtLauncher/QtLauncher.qrc b/WebKitTools/QtLauncher/QtLauncher.qrc
new file mode 100644
index 0000000..ffe77b0
--- /dev/null
+++ b/WebKitTools/QtLauncher/QtLauncher.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>useragentlist.txt</file>
+ </qresource>
+</RCC>
diff --git a/WebKitTools/QtLauncher/main.cpp b/WebKitTools/QtLauncher/main.cpp
index 1203ce7..a03d135 100644
--- a/WebKitTools/QtLauncher/main.cpp
+++ b/WebKitTools/QtLauncher/main.cpp
@@ -32,6 +32,11 @@
#include <QtGui>
#include <QtNetwork/QNetworkRequest>
+
+#if defined(QT_CONFIGURED_WITH_OPENGL)
+#include <QtOpenGL/QGLWidget>
+#endif
+
#if !defined(QT_NO_PRINTER)
#include <QPrintPreviewDialog>
#endif
@@ -43,7 +48,6 @@
#include <QDebug>
#include <cstdio>
-#include "mainwindow.h"
#include <qevent.h>
#include <qwebelement.h>
#include <qwebframe.h>
@@ -54,11 +58,13 @@
#include <qx11info_x11.h>
#endif
+#include "mainwindow.h"
#include "urlloader.h"
#include "utils.h"
#include "webinspector.h"
#include "webpage.h"
#include "webview.h"
+#include "../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h"
#ifdef Q_WS_MAEMO_5
#include <X11/Xatom.h>
@@ -66,23 +72,32 @@
#undef KeyPress
#endif
-#ifndef NDEBUG
-void QWEBKIT_EXPORT qt_drt_garbageCollector_collect();
-#endif
-
-
+static const int gExitClickArea = 80;
static bool gUseGraphicsView = false;
-static bool gUseCompositing = false;
+static bool gUseCompositing = true;
static bool gCacheWebView = false;
static bool gShowFrameRate = false;
static QGraphicsView::ViewportUpdateMode gViewportUpdateMode = QGraphicsView::MinimalViewportUpdate;
+static QUrl gInspectorUrl;
+static bool gResizesToContents = false;
+static bool gUseTiledBackingStore = false;
+
+#if defined(Q_WS_MAEMO_5) || defined(Q_WS_S60)
+static bool gUseFrameFlattening = true;
+#else
+static bool gUseFrameFlattening = false;
+#endif
+
+#if defined(QT_CONFIGURED_WITH_OPENGL)
+static bool gUseQGLWidgetViewport = false;
+#endif
class LauncherWindow : public MainWindow {
Q_OBJECT
public:
- LauncherWindow(QString url = QString());
+ LauncherWindow(LauncherWindow* other = 0, bool shareScene = false);
virtual ~LauncherWindow();
virtual void keyPressEvent(QKeyEvent* event);
@@ -90,9 +105,10 @@ public:
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
void sendTouchEvent();
- bool eventFilter(QObject* obj, QEvent* event);
#endif
+ bool eventFilter(QObject* obj, QEvent* event);
+
protected slots:
void loadStarted();
void loadFinished();
@@ -103,6 +119,7 @@ protected slots:
void zoomOut();
void resetZoom();
void toggleZoomTextOnly(bool on);
+ void zoomAnimationFinished();
void print();
void screenshot();
@@ -116,74 +133,159 @@ protected slots:
void setTouchMocking(bool on);
void toggleAcceleratedCompositing(bool toggle);
+ void toggleTiledBackingStore(bool toggle);
+ void toggleResizesToContents(bool toggle);
+
+ void toggleWebGL(bool toggle);
void initializeView(bool useGraphicsView = false);
+ void toggleSpatialNavigation(bool b);
+ void toggleFullScreenMode(bool enable);
+ void showFPS(bool enable);
+ void changeViewportUpdateMode(int mode);
+ void toggleFrameFlattening(bool toggle);
+ void toggleInterruptingJavaScriptEnabled(bool enable);
+
+#if defined(QT_CONFIGURED_WITH_OPENGL)
+ void toggleQGLWidgetViewport(bool enable);
+#endif
+
+ void showUserAgentDialog();
public slots:
- void newWindow(const QString& url = QString());
+ LauncherWindow* newWindow();
+ LauncherWindow* cloneWindow();
+ void updateFPS(int fps);
+
+signals:
+ void enteredFullScreenMode(bool on);
private:
- // create the status bar, tool bar & menu
- void setupUI();
+ void createChrome();
+ void applyZoom();
private:
- QVector<int> zoomLevels;
- int currentZoom;
+ QVector<int> m_zoomLevels;
+ int m_currentZoom;
QWidget* m_view;
- WebInspector* inspector;
+ WebInspector* m_inspector;
- QAction* formatMenuAction;
- QAction* flipAnimated;
- QAction* flipYAnimated;
+ QAction* m_formatMenuAction;
+ QAction* m_flipAnimated;
+ QAction* m_flipYAnimated;
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
- QList<QTouchEvent::TouchPoint> touchPoints;
- bool touchMocking;
+ QPropertyAnimation* m_zoomAnimation;
+ QList<QTouchEvent::TouchPoint> m_touchPoints;
+ bool m_touchMocking;
#endif
+
+ void init(bool useGraphicsView = false);
+ bool isGraphicsBased() const;
+ void applyPrefs(LauncherWindow* other = 0);
};
+LauncherWindow::LauncherWindow(LauncherWindow* other, bool shareScene)
+ : MainWindow()
+ , m_currentZoom(100)
+ , m_view(0)
+ , m_inspector(0)
+ , m_formatMenuAction(0)
+ , m_flipAnimated(0)
+ , m_flipYAnimated(0)
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+ , m_zoomAnimation(0)
+#endif
+{
+ if (other) {
+ init(other->isGraphicsBased());
+ applyPrefs(other);
+ if (shareScene && other->isGraphicsBased()) {
+ QGraphicsView* otherView = static_cast<QGraphicsView*>(other->m_view);
+ static_cast<QGraphicsView*>(m_view)->setScene(otherView->scene());
+ }
+ } else {
+ init(gUseGraphicsView);
+ applyPrefs();
+ }
+
+ createChrome();
+}
-LauncherWindow::LauncherWindow(QString url)
- : MainWindow(url)
- , currentZoom(100)
+LauncherWindow::~LauncherWindow()
+{
+ grabZoomKeys(false);
+}
+
+void LauncherWindow::init(bool useGraphicsView)
{
QSplitter* splitter = new QSplitter(Qt::Vertical, this);
setCentralWidget(splitter);
#if defined(Q_WS_S60)
- showMaximized();
+ setWindowState(Qt::WindowMaximized);
#else
+ setWindowState(Qt::WindowNoState);
resize(800, 600);
#endif
- m_view = 0;
- initializeView();
+ initializeView(useGraphicsView);
connect(page(), SIGNAL(loadStarted()), this, SLOT(loadStarted()));
connect(page(), SIGNAL(loadFinished(bool)), this, SLOT(loadFinished()));
connect(page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)),
this, SLOT(showLinkHover(const QString&, const QString&)));
+ connect(this, SIGNAL(enteredFullScreenMode(bool)), this, SLOT(toggleFullScreenMode(bool)));
- inspector = new WebInspector(splitter);
- inspector->setPage(page());
- inspector->hide();
- connect(this, SIGNAL(destroyed()), inspector, SLOT(deleteLater()));
+ if (!gInspectorUrl.isEmpty())
+ page()->settings()->setInspectorUrl(gInspectorUrl);
- setupUI();
+ m_inspector = new WebInspector(splitter);
+ m_inspector->setPage(page());
+ m_inspector->hide();
+ connect(this, SIGNAL(destroyed()), m_inspector, SLOT(deleteLater()));
// the zoom values are chosen to be like in Mozilla Firefox 3
- zoomLevels << 30 << 50 << 67 << 80 << 90;
- zoomLevels << 100;
- zoomLevels << 110 << 120 << 133 << 150 << 170 << 200 << 240 << 300;
+ m_zoomLevels << 30 << 50 << 67 << 80 << 90;
+ m_zoomLevels << 100;
+ m_zoomLevels << 110 << 120 << 133 << 150 << 170 << 200 << 240 << 300;
grabZoomKeys(true);
+}
- load(url);
+bool LauncherWindow::isGraphicsBased() const
+{
+ return bool(qobject_cast<QGraphicsView*>(m_view));
}
-LauncherWindow::~LauncherWindow()
+inline void applySetting(QWebSettings::WebAttribute type, QWebSettings* settings, QWebSettings* other, bool defaultValue)
{
- grabZoomKeys(false);
+ settings->setAttribute(type, other ? other->testAttribute(type) : defaultValue);
+}
+
+void LauncherWindow::applyPrefs(LauncherWindow* source)
+{
+ QWebSettings* other = source ? source->page()->settings() : 0;
+ QWebSettings* settings = page()->settings();
+
+ applySetting(QWebSettings::AcceleratedCompositingEnabled, settings, other, gUseCompositing);
+ applySetting(QWebSettings::TiledBackingStoreEnabled, settings, other, gUseTiledBackingStore);
+ applySetting(QWebSettings::WebGLEnabled, settings, other, false);
+ applySetting(QWebSettings::FrameFlatteningEnabled, settings, other, gUseFrameFlattening);
+
+ if (!isGraphicsBased())
+ return;
+
+ WebViewGraphicsBased* view = static_cast<WebViewGraphicsBased*>(m_view);
+ WebViewGraphicsBased* otherView = source ? qobject_cast<WebViewGraphicsBased*>(source->m_view) : 0;
+
+ view->setViewportUpdateMode(otherView ? otherView->viewportUpdateMode() : gViewportUpdateMode);
+ view->setFrameRateMeasurementEnabled(otherView ? otherView->frameRateMeasurementEnabled() : gShowFrameRate);
+
+ if (otherView)
+ view->setItemCacheMode(otherView->itemCacheMode());
+ else
+ view->setItemCacheMode(gCacheWebView ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache);
}
void LauncherWindow::keyPressEvent(QKeyEvent* event)
@@ -225,31 +327,45 @@ void LauncherWindow::grabZoomKeys(bool grab)
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
void LauncherWindow::sendTouchEvent()
{
- if (touchPoints.isEmpty())
+ if (m_touchPoints.isEmpty())
return;
QEvent::Type type = QEvent::TouchUpdate;
- if (touchPoints.size() == 1) {
- if (touchPoints[0].state() == Qt::TouchPointReleased)
+ if (m_touchPoints.size() == 1) {
+ if (m_touchPoints[0].state() == Qt::TouchPointReleased)
type = QEvent::TouchEnd;
- else if (touchPoints[0].state() == Qt::TouchPointPressed)
+ else if (m_touchPoints[0].state() == Qt::TouchPointPressed)
type = QEvent::TouchBegin;
}
QTouchEvent touchEv(type);
- touchEv.setTouchPoints(touchPoints);
+ touchEv.setTouchPoints(m_touchPoints);
QCoreApplication::sendEvent(page(), &touchEv);
// After sending the event, remove all touchpoints that were released
- if (touchPoints[0].state() == Qt::TouchPointReleased)
- touchPoints.removeAt(0);
- if (touchPoints.size() > 1 && touchPoints[1].state() == Qt::TouchPointReleased)
- touchPoints.removeAt(1);
+ if (m_touchPoints[0].state() == Qt::TouchPointReleased)
+ m_touchPoints.removeAt(0);
+ if (m_touchPoints.size() > 1 && m_touchPoints[1].state() == Qt::TouchPointReleased)
+ m_touchPoints.removeAt(1);
}
+#endif // QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
bool LauncherWindow::eventFilter(QObject* obj, QEvent* event)
{
- if (!touchMocking || obj != m_view)
+ // If click pos is the bottom right corner (square with size defined by gExitClickArea)
+ // and the window is on FullScreen, the window must return to its original state.
+ if (event->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent* ev = static_cast<QMouseEvent*>(event);
+ if (windowState() == Qt::WindowFullScreen
+ && ev->pos().x() > (width() - gExitClickArea)
+ && ev->pos().y() > (height() - gExitClickArea)) {
+
+ emit enteredFullScreenMode(false);
+ }
+ }
+
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+ if (!m_touchMocking)
return QObject::eventFilter(obj, event);
if (event->type() == QEvent::MouseButtonPress
@@ -276,12 +392,12 @@ bool LauncherWindow::eventFilter(QObject* obj, QEvent* event)
touchPoint.setPressure(1);
// If the point already exists, update it. Otherwise create it.
- if (touchPoints.size() > 0 && !touchPoints[0].id())
- touchPoints[0] = touchPoint;
- else if (touchPoints.size() > 1 && !touchPoints[1].id())
- touchPoints[1] = touchPoint;
+ if (m_touchPoints.size() > 0 && !m_touchPoints[0].id())
+ m_touchPoints[0] = touchPoint;
+ else if (m_touchPoints.size() > 1 && !m_touchPoints[1].id())
+ m_touchPoints[1] = touchPoint;
else
- touchPoints.append(touchPoint);
+ m_touchPoints.append(touchPoint);
sendTouchEvent();
} else if (event->type() == QEvent::KeyPress
@@ -289,12 +405,12 @@ bool LauncherWindow::eventFilter(QObject* obj, QEvent* event)
&& static_cast<QKeyEvent*>(event)->modifiers() == Qt::ControlModifier) {
// If the keyboard point is already pressed, release it.
- // Otherwise create it and append to touchPoints.
- if (touchPoints.size() > 0 && touchPoints[0].id() == 1) {
- touchPoints[0].setState(Qt::TouchPointReleased);
+ // Otherwise create it and append to m_touchPoints.
+ if (m_touchPoints.size() > 0 && m_touchPoints[0].id() == 1) {
+ m_touchPoints[0].setState(Qt::TouchPointReleased);
sendTouchEvent();
- } else if (touchPoints.size() > 1 && touchPoints[1].id() == 1) {
- touchPoints[1].setState(Qt::TouchPointReleased);
+ } else if (m_touchPoints.size() > 1 && m_touchPoints[1].id() == 1) {
+ m_touchPoints[1].setState(Qt::TouchPointReleased);
sendTouchEvent();
} else {
QTouchEvent::TouchPoint touchPoint;
@@ -303,16 +419,17 @@ bool LauncherWindow::eventFilter(QObject* obj, QEvent* event)
touchPoint.setScreenPos(QCursor::pos());
touchPoint.setPos(m_view->mapFromGlobal(QCursor::pos()));
touchPoint.setPressure(1);
- touchPoints.append(touchPoint);
+ m_touchPoints.append(touchPoint);
sendTouchEvent();
// After sending the event, change the touchpoint state to stationary
- touchPoints.last().setState(Qt::TouchPointStationary);
+ m_touchPoints.last().setState(Qt::TouchPointStationary);
}
}
+#endif // QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+
return false;
}
-#endif // QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
void LauncherWindow::loadStarted()
{
@@ -322,7 +439,7 @@ void LauncherWindow::loadStarted()
void LauncherWindow::loadFinished()
{
QUrl url = page()->mainFrame()->url();
- setAddressUrl(url.toString());
+ setAddressUrl(url.toString(QUrl::RemoveUserInfo));
addCompleterEntry(url);
}
@@ -337,30 +454,62 @@ void LauncherWindow::showLinkHover(const QString &link, const QString &toolTip)
#endif
}
+void LauncherWindow::zoomAnimationFinished()
+{
+ if (!isGraphicsBased())
+ return;
+ QGraphicsWebView* view = static_cast<WebViewGraphicsBased*>(m_view)->graphicsWebView();
+ view->setTiledBackingStoreFrozen(false);
+}
+
+void LauncherWindow::applyZoom()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+ if (isGraphicsBased() && page()->settings()->testAttribute(QWebSettings::TiledBackingStoreEnabled)) {
+ QGraphicsWebView* view = static_cast<WebViewGraphicsBased*>(m_view)->graphicsWebView();
+ view->setTiledBackingStoreFrozen(true);
+ if (!m_zoomAnimation) {
+ m_zoomAnimation = new QPropertyAnimation(view, "scale");
+ m_zoomAnimation->setStartValue(view->scale());
+ connect(m_zoomAnimation, SIGNAL(finished()), this, SLOT(zoomAnimationFinished()));
+ } else {
+ m_zoomAnimation->stop();
+ m_zoomAnimation->setStartValue(m_zoomAnimation->currentValue());
+ }
+
+ m_zoomAnimation->setDuration(300);
+ m_zoomAnimation->setEndValue(qreal(m_currentZoom) / 100.);
+ m_zoomAnimation->start();
+ return;
+ }
+#endif
+ page()->mainFrame()->setZoomFactor(qreal(m_currentZoom) / 100.0);
+}
+
void LauncherWindow::zoomIn()
{
- int i = zoomLevels.indexOf(currentZoom);
+ int i = m_zoomLevels.indexOf(m_currentZoom);
Q_ASSERT(i >= 0);
- if (i < zoomLevels.count() - 1)
- currentZoom = zoomLevels[i + 1];
+ if (i < m_zoomLevels.count() - 1)
+ m_currentZoom = m_zoomLevels[i + 1];
- page()->mainFrame()->setZoomFactor(qreal(currentZoom) / 100.0);
+ applyZoom();
}
void LauncherWindow::zoomOut()
{
- int i = zoomLevels.indexOf(currentZoom);
+ int i = m_zoomLevels.indexOf(m_currentZoom);
Q_ASSERT(i >= 0);
if (i > 0)
- currentZoom = zoomLevels[i - 1];
+ m_currentZoom = m_zoomLevels[i - 1];
- page()->mainFrame()->setZoomFactor(qreal(currentZoom) / 100.0);
+ applyZoom();
}
void LauncherWindow::resetZoom()
{
- currentZoom = 100;
- page()->mainFrame()->setZoomFactor(1.0);
+ m_currentZoom = 100;
+ page()->mainFrame()->setZoomFactor(1.0);
}
void LauncherWindow::toggleZoomTextOnly(bool b)
@@ -373,7 +522,7 @@ void LauncherWindow::print()
#if !defined(QT_NO_PRINTER)
QPrintPreviewDialog dlg(this);
connect(&dlg, SIGNAL(paintRequested(QPrinter*)),
- m_view, SLOT(print(QPrinter*)));
+ page()->mainFrame(), SLOT(print(QPrinter*)));
dlg.exec();
#endif
}
@@ -397,7 +546,7 @@ void LauncherWindow::screenshot()
void LauncherWindow::setEditable(bool on)
{
page()->setContentEditable(on);
- formatMenuAction->setVisible(on);
+ m_formatMenuAction->setVisible(on);
}
/*
@@ -436,15 +585,31 @@ void LauncherWindow::selectElements()
void LauncherWindow::setTouchMocking(bool on)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
- touchMocking = on;
+ m_touchMocking = on;
#endif
}
void LauncherWindow::toggleAcceleratedCompositing(bool toggle)
{
+ gUseCompositing = toggle;
page()->settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, toggle);
}
+void LauncherWindow::toggleTiledBackingStore(bool toggle)
+{
+ page()->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, toggle);
+}
+
+void LauncherWindow::toggleResizesToContents(bool toggle)
+{
+ static_cast<WebViewGraphicsBased*>(m_view)->setResizesToContents(toggle);
+}
+
+void LauncherWindow::toggleWebGL(bool toggle)
+{
+ page()->settings()->setAttribute(QWebSettings::WebGLEnabled, toggle);
+}
+
void LauncherWindow::initializeView(bool useGraphicsView)
{
delete m_view;
@@ -454,38 +619,177 @@ void LauncherWindow::initializeView(bool useGraphicsView)
if (!useGraphicsView) {
WebViewTraditional* view = new WebViewTraditional(splitter);
view->setPage(page());
+
+ view->installEventFilter(this);
+
m_view = view;
} else {
WebViewGraphicsBased* view = new WebViewGraphicsBased(splitter);
view->setPage(page());
- view->setViewportUpdateMode(gViewportUpdateMode);
- view->setItemCacheMode(gCacheWebView ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache);
- if (gShowFrameRate)
- view->enableFrameRateMeasurement();
- page()->settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, gUseCompositing);
- if (flipAnimated)
- connect(flipAnimated, SIGNAL(triggered()), view, SLOT(animatedFlip()));
+ if (m_flipAnimated)
+ connect(m_flipAnimated, SIGNAL(triggered()), view, SLOT(animatedFlip()));
+
+ if (m_flipYAnimated)
+ connect(m_flipYAnimated, SIGNAL(triggered()), view, SLOT(animatedYFlip()));
- if (flipYAnimated)
- connect(flipYAnimated, SIGNAL(triggered()), view, SLOT(animatedYFlip()));
+ connect(view, SIGNAL(currentFPSUpdated(int)), this, SLOT(updateFPS(int)));
+
+ view->installEventFilter(this);
+ // The implementation of QAbstractScrollArea::eventFilter makes us need
+ // to install the event filter also on the viewport of a QGraphicsView.
+ view->viewport()->installEventFilter(this);
m_view = view;
}
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
- m_view->installEventFilter(this);
- touchMocking = false;
+ m_touchMocking = false;
+#endif
+}
+
+void LauncherWindow::toggleSpatialNavigation(bool b)
+{
+ page()->settings()->setAttribute(QWebSettings::SpatialNavigationEnabled, b);
+}
+
+void LauncherWindow::toggleFullScreenMode(bool enable)
+{
+ if (enable)
+ setWindowState(Qt::WindowFullScreen);
+ else {
+#if defined(Q_WS_S60)
+ setWindowState(Qt::WindowMaximized);
+#else
+ setWindowState(Qt::WindowNoState);
+#endif
+ }
+}
+
+void LauncherWindow::showFPS(bool enable)
+{
+ if (!isGraphicsBased())
+ return;
+
+ gShowFrameRate = enable;
+ WebViewGraphicsBased* view = static_cast<WebViewGraphicsBased*>(m_view);
+ view->setFrameRateMeasurementEnabled(enable);
+
+ if (!enable) {
+#if defined(Q_WS_MAEMO_5) && defined(Q_WS_S60)
+ setWindowTitle("");
+#else
+ statusBar()->clearMessage();
#endif
+ }
+}
+
+void LauncherWindow::changeViewportUpdateMode(int mode)
+{
+ gViewportUpdateMode = QGraphicsView::ViewportUpdateMode(mode);
+
+ if (!isGraphicsBased())
+ return;
+
+ WebViewGraphicsBased* view = static_cast<WebViewGraphicsBased*>(m_view);
+ view->setViewportUpdateMode(gViewportUpdateMode);
+}
+
+void LauncherWindow::toggleFrameFlattening(bool toggle)
+{
+ gUseFrameFlattening = toggle;
+ page()->settings()->setAttribute(QWebSettings::FrameFlatteningEnabled, toggle);
}
-void LauncherWindow::newWindow(const QString& url)
+void LauncherWindow::toggleInterruptingJavaScriptEnabled(bool enable)
+{
+ page()->setInterruptingJavaScriptEnabled(enable);
+}
+
+#if defined(QT_CONFIGURED_WITH_OPENGL)
+void LauncherWindow::toggleQGLWidgetViewport(bool enable)
+{
+ if (!isGraphicsBased())
+ return;
+
+ gUseQGLWidgetViewport = enable;
+ WebViewGraphicsBased* view = static_cast<WebViewGraphicsBased*>(m_view);
+
+ view->setViewport(enable ? new QGLWidget() : 0);
+}
+#endif
+
+void LauncherWindow::showUserAgentDialog()
{
- LauncherWindow* mw = new LauncherWindow(url);
+ QStringList items;
+ QFile file(":/useragentlist.txt");
+ if (file.open(QIODevice::ReadOnly)) {
+ while (!file.atEnd())
+ items << file.readLine().trimmed();
+ file.close();
+ }
+
+ QSettings settings;
+ QString customUserAgent = settings.value("CustomUserAgent").toString();
+ if (!items.contains(customUserAgent) && !customUserAgent.isEmpty())
+ items << customUserAgent;
+
+ QDialog* dialog = new QDialog(this);
+ dialog->setWindowTitle("Change User Agent");
+
+ QVBoxLayout* layout = new QVBoxLayout(dialog);
+ dialog->setLayout(layout);
+
+ QComboBox* combo = new QComboBox(dialog);
+ combo->setMaximumWidth(size().width() * 0.7);
+ combo->setEditable(true);
+ combo->insertItems(0, items);
+ layout->addWidget(combo);
+
+ int index = combo->findText(page()->userAgentForUrl(QUrl()));
+ combo->setCurrentIndex(index);
+
+ QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel, Qt::Horizontal, dialog);
+ connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
+ layout->addWidget(buttonBox);
+
+ if (dialog->exec() && !combo->currentText().isEmpty()) {
+ page()->setUserAgent(combo->currentText());
+ if (!items.contains(combo->currentText()))
+ settings.setValue("CustomUserAgent", combo->currentText());
+ }
+
+ delete dialog;
+}
+
+LauncherWindow* LauncherWindow::newWindow()
+{
+ LauncherWindow* mw = new LauncherWindow(this, false);
+ mw->show();
+ return mw;
+}
+
+LauncherWindow* LauncherWindow::cloneWindow()
+{
+ LauncherWindow* mw = new LauncherWindow(this, true);
mw->show();
+ return mw;
}
-void LauncherWindow::setupUI()
+void LauncherWindow::updateFPS(int fps)
+{
+ QString fpsStatusText = QString("Current FPS: %1").arg(fps);
+
+#if defined(Q_WS_MAEMO_5) && defined(Q_WS_S60)
+ setWindowTitle(fpsStatusText);
+#else
+ statusBar()->showMessage(fpsStatusText);
+#endif
+}
+
+void LauncherWindow::createChrome()
{
QMenu* fileMenu = menuBar()->addMenu("&File");
fileMenu->addAction("New Window", this, SLOT(newWindow()), QKeySequence::New);
@@ -523,8 +827,8 @@ void LauncherWindow::setupUI()
// viewMenu->addAction("Dump plugins", this, SLOT(dumpPlugins()));
QMenu* formatMenu = new QMenu("F&ormat", this);
- formatMenuAction = menuBar()->addMenu(formatMenu);
- formatMenuAction->setVisible(false);
+ m_formatMenuAction = menuBar()->addMenu(formatMenu);
+ m_formatMenuAction->setVisible(false);
formatMenu->addAction(page()->action(QWebPage::ToggleBold));
formatMenu->addAction(page()->action(QWebPage::ToggleItalic));
formatMenu->addAction(page()->action(QWebPage::ToggleUnderline));
@@ -537,11 +841,20 @@ void LauncherWindow::setupUI()
zoomOut->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Minus));
resetZoom->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
+ QMenu* windowMenu = menuBar()->addMenu("&Window");
+ QAction* toggleFullScreen = windowMenu->addAction("Toggle FullScreen", this, SIGNAL(enteredFullScreenMode(bool)));
+ toggleFullScreen->setCheckable(true);
+ toggleFullScreen->setChecked(false);
+
+ // when exit fullscreen mode by clicking on the exit area (bottom right corner) we must
+ // uncheck the Toggle FullScreen action
+ toggleFullScreen->connect(this, SIGNAL(enteredFullScreenMode(bool)), SLOT(setChecked(bool)));
+
QMenu* toolsMenu = menuBar()->addMenu("&Develop");
toolsMenu->addAction("Select Elements...", this, SLOT(selectElements()));
- QAction* showInspectorAction = toolsMenu->addAction("Show Web Inspector", inspector, SLOT(setVisible(bool)), QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_I));
+ QAction* showInspectorAction = toolsMenu->addAction("Show Web Inspector", m_inspector, SLOT(setVisible(bool)), QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_I));
showInspectorAction->setCheckable(true);
- showInspectorAction->connect(inspector, SIGNAL(visibleChanged(bool)), SLOT(setChecked(bool)));
+ showInspectorAction->connect(m_inspector, SIGNAL(visibleChanged(bool)), SLOT(setChecked(bool)));
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
QAction* touchMockAction = toolsMenu->addAction("Toggle multitouch mocking", this, SLOT(setTouchMocking(bool)));
@@ -549,26 +862,131 @@ void LauncherWindow::setupUI()
touchMockAction->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_T));
#endif
+ QWebSettings* settings = page()->settings();
+
QMenu* graphicsViewMenu = toolsMenu->addMenu("QGraphicsView");
QAction* toggleGraphicsView = graphicsViewMenu->addAction("Toggle use of QGraphicsView", this, SLOT(initializeView(bool)));
toggleGraphicsView->setCheckable(true);
- toggleGraphicsView->setChecked(false);
+ toggleGraphicsView->setChecked(isGraphicsBased());
+
+ QAction* toggleWebGL = toolsMenu->addAction("Toggle WebGL", this, SLOT(toggleWebGL(bool)));
+ toggleWebGL->setCheckable(true);
+ toggleWebGL->setChecked(settings->testAttribute(QWebSettings::WebGLEnabled));
QAction* toggleAcceleratedCompositing = graphicsViewMenu->addAction("Toggle Accelerated Compositing", this, SLOT(toggleAcceleratedCompositing(bool)));
toggleAcceleratedCompositing->setCheckable(true);
- toggleAcceleratedCompositing->setChecked(false);
- toggleAcceleratedCompositing->setEnabled(false);
+ toggleAcceleratedCompositing->setChecked(settings->testAttribute(QWebSettings::AcceleratedCompositingEnabled));
+ toggleAcceleratedCompositing->setEnabled(isGraphicsBased());
toggleAcceleratedCompositing->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+ QAction* toggleResizesToContents = graphicsViewMenu->addAction("Toggle Resizes To Contents Mode", this, SLOT(toggleResizesToContents(bool)));
+ toggleResizesToContents->setCheckable(true);
+ toggleResizesToContents->setChecked(false);
+ toggleResizesToContents->setEnabled(false);
+ toggleResizesToContents->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+
+ QAction* toggleTiledBackingStore = graphicsViewMenu->addAction("Toggle Tiled Backing Store", this, SLOT(toggleTiledBackingStore(bool)));
+ toggleTiledBackingStore->setCheckable(true);
+ toggleTiledBackingStore->setChecked(false);
+ toggleTiledBackingStore->setEnabled(false);
+ toggleTiledBackingStore->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+
+ QAction* spatialNavigationAction = toolsMenu->addAction("Toggle Spatial Navigation", this, SLOT(toggleSpatialNavigation(bool)));
+ spatialNavigationAction->setCheckable(true);
+ spatialNavigationAction->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_S));
+
+ QAction* toggleFrameFlattening = toolsMenu->addAction("Toggle Frame Flattening", this, SLOT(toggleFrameFlattening(bool)));
+ toggleFrameFlattening->setCheckable(true);
+ toggleFrameFlattening->setChecked(settings->testAttribute(QWebSettings::FrameFlatteningEnabled));
+
+ QAction* toggleInterruptingJavaScripteEnabled = toolsMenu->addAction("Enable interrupting js scripts", this, SLOT(toggleInterruptingJavaScriptEnabled(bool)));
+ toggleInterruptingJavaScripteEnabled->setCheckable(true);
+ toggleInterruptingJavaScripteEnabled->setChecked(false);
+
+#if defined(QT_CONFIGURED_WITH_OPENGL)
+ QAction* toggleQGLWidgetViewport = graphicsViewMenu->addAction("Toggle use of QGLWidget Viewport", this, SLOT(toggleQGLWidgetViewport(bool)));
+ toggleQGLWidgetViewport->setCheckable(true);
+ toggleQGLWidgetViewport->setChecked(gUseQGLWidgetViewport);
+ toggleQGLWidgetViewport->setEnabled(isGraphicsBased());
+ toggleQGLWidgetViewport->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+#endif
+
+ QAction* userAgentAction = toolsMenu->addAction("Change User Agent", this, SLOT(showUserAgentDialog()));
+ userAgentAction->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_U));
+
graphicsViewMenu->addSeparator();
- flipAnimated = graphicsViewMenu->addAction("Animated Flip");
- flipAnimated->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
- flipAnimated->setEnabled(false);
+ m_flipAnimated = graphicsViewMenu->addAction("Animated Flip");
+ m_flipAnimated->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+ m_flipAnimated->setEnabled(isGraphicsBased());
+
+ m_flipYAnimated = graphicsViewMenu->addAction("Animated Y-Flip");
+ m_flipYAnimated->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+ m_flipYAnimated->setEnabled(isGraphicsBased());
- flipYAnimated = graphicsViewMenu->addAction("Animated Y-Flip");
- flipYAnimated->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
- flipYAnimated->setEnabled(false);
+ if (isGraphicsBased()) {
+ WebViewGraphicsBased* view = static_cast<WebViewGraphicsBased*>(m_view);
+ connect(m_flipAnimated, SIGNAL(triggered()), view, SLOT(animatedFlip()));
+ connect(m_flipYAnimated, SIGNAL(triggered()), view, SLOT(animatedYFlip()));
+ }
+
+ graphicsViewMenu->addSeparator();
+
+ QAction* cloneWindow = graphicsViewMenu->addAction("Clone Window", this, SLOT(cloneWindow()));
+ cloneWindow->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+ cloneWindow->setEnabled(isGraphicsBased());
+
+ QAction* showFPS = graphicsViewMenu->addAction("Show FPS", this, SLOT(showFPS(bool)));
+ showFPS->setCheckable(true);
+ showFPS->setEnabled(isGraphicsBased());
+ showFPS->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+ showFPS->setChecked(gShowFrameRate);
+
+ QMenu* viewportUpdateMenu = graphicsViewMenu->addMenu("Change Viewport Update Mode");
+ viewportUpdateMenu->setEnabled(isGraphicsBased());
+ viewportUpdateMenu->connect(toggleGraphicsView, SIGNAL(toggled(bool)), SLOT(setEnabled(bool)));
+
+ QAction* fullUpdate = viewportUpdateMenu->addAction("FullViewportUpdate");
+ fullUpdate->setCheckable(true);
+ fullUpdate->setChecked((gViewportUpdateMode == QGraphicsView::FullViewportUpdate) ? true : false);
+
+ QAction* minimalUpdate = viewportUpdateMenu->addAction("MinimalViewportUpdate");
+ minimalUpdate->setCheckable(true);
+ minimalUpdate->setChecked((gViewportUpdateMode == QGraphicsView::MinimalViewportUpdate) ? true : false);
+
+ QAction* smartUpdate = viewportUpdateMenu->addAction("SmartViewportUpdate");
+ smartUpdate->setCheckable(true);
+ smartUpdate->setChecked((gViewportUpdateMode == QGraphicsView::SmartViewportUpdate) ? true : false);
+
+ QAction* boundingRectUpdate = viewportUpdateMenu->addAction("BoundingRectViewportUpdate");
+ boundingRectUpdate->setCheckable(true);
+ boundingRectUpdate->setChecked((gViewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) ? true : false);
+
+ QAction* noUpdate = viewportUpdateMenu->addAction("NoViewportUpdate");
+ noUpdate->setCheckable(true);
+ noUpdate->setChecked((gViewportUpdateMode == QGraphicsView::NoViewportUpdate) ? true : false);
+
+ QSignalMapper* signalMapper = new QSignalMapper(viewportUpdateMenu);
+ signalMapper->setMapping(fullUpdate, QGraphicsView::FullViewportUpdate);
+ signalMapper->setMapping(minimalUpdate, QGraphicsView::MinimalViewportUpdate);
+ signalMapper->setMapping(smartUpdate, QGraphicsView::SmartViewportUpdate);
+ signalMapper->setMapping(boundingRectUpdate, QGraphicsView::BoundingRectViewportUpdate);
+ signalMapper->setMapping(noUpdate, QGraphicsView::NoViewportUpdate);
+
+ connect(fullUpdate, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(minimalUpdate, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(smartUpdate, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(boundingRectUpdate, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(noUpdate, SIGNAL(triggered()), signalMapper, SLOT(map()));
+
+ connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(changeViewportUpdateMode(int)));
+
+ QActionGroup* viewportUpdateModeActions = new QActionGroup(viewportUpdateMenu);
+ viewportUpdateModeActions->addAction(fullUpdate);
+ viewportUpdateModeActions->addAction(minimalUpdate);
+ viewportUpdateModeActions->addAction(smartUpdate);
+ viewportUpdateModeActions->addAction(boundingRectUpdate);
+ viewportUpdateModeActions->addAction(noUpdate);
}
QWebPage* WebPage::createWindow(QWebPage::WebWindowType type)
@@ -602,7 +1020,7 @@ int launcherMain(const QApplication& app)
{
#ifndef NDEBUG
int retVal = app.exec();
- qt_drt_garbageCollector_collect();
+ DumpRenderTreeSupportQt::garbageCollectorCollect();
QWebSettings::clearMemoryCaches();
return retVal;
#else
@@ -639,7 +1057,7 @@ void LauncherApplication::applyDefaultSettings()
}
LauncherApplication::LauncherApplication(int& argc, char** argv)
- : QApplication(argc, argv)
+ : QApplication(argc, argv, QApplication::GuiServer)
, m_isRobotized(false)
{
// To allow QWebInspector's configuration persistence
@@ -673,11 +1091,14 @@ void LauncherApplication::handleUserOptions()
if (args.contains("-help")) {
qDebug() << "Usage:" << programName.toLatin1().data()
<< "[-graphicsbased]"
- << "[-compositing]"
+ << "[-no-compositing]"
<< QString("[-viewport-update-mode %1]").arg(formatKeys(updateModes)).toLatin1().data()
<< "[-cache-webview]"
<< "[-show-fps]"
<< "[-r list]"
+ << "[-inspector-url location]"
+ << "[-tiled-backing-store]"
+ << "[-resizes-to-contents]"
<< "URLs";
appQuit(0);
}
@@ -685,9 +1106,9 @@ void LauncherApplication::handleUserOptions()
if (args.contains("-graphicsbased"))
gUseGraphicsView = true;
- if (args.contains("-compositing")) {
- requiresGraphicsView("-compositing");
- gUseCompositing = true;
+ if (args.contains("-no-compositing")) {
+ requiresGraphicsView("-no-compositing");
+ gUseCompositing = false;
}
if (args.contains("-show-fps")) {
@@ -700,6 +1121,16 @@ void LauncherApplication::handleUserOptions()
gCacheWebView = true;
}
+ if (args.contains("-tiled-backing-store")) {
+ requiresGraphicsView("-tiled-backing-store");
+ gUseTiledBackingStore = true;
+ }
+
+ if (args.contains("-resizes-to-contents")) {
+ requiresGraphicsView("-resizes-to-contents");
+ gResizesToContents = true;
+ }
+
QString arg1("-viewport-update-mode");
int modeIndex = args.indexOf(arg1);
if (modeIndex != -1) {
@@ -715,6 +1146,11 @@ void LauncherApplication::handleUserOptions()
gViewportUpdateMode = static_cast<QGraphicsView::ViewportUpdateMode>(idx);
}
+ QString inspectorUrlArg("-inspector-url");
+ int inspectorUrlIndex = args.indexOf(inspectorUrlArg);
+ if (inspectorUrlIndex != -1)
+ gInspectorUrl = takeOptionValue(&args, inspectorUrlIndex);
+
int robotIndex = args.indexOf("-r");
if (robotIndex != -1) {
QString listFile = takeOptionValue(&args, robotIndex);
@@ -758,10 +1194,13 @@ int main(int argc, char **argv)
LauncherWindow* window = 0;
foreach (QString url, urls) {
+ LauncherWindow* newWindow;
if (!window)
- window = new LauncherWindow(url);
+ newWindow = window = new LauncherWindow();
else
- window->newWindow(url);
+ newWindow = window->newWindow();
+
+ newWindow->load(url);
}
window->show();
diff --git a/WebKitTools/QtLauncher/mainwindow.cpp b/WebKitTools/QtLauncher/mainwindow.cpp
index 47755ec..aa6aa26 100644
--- a/WebKitTools/QtLauncher/mainwindow.cpp
+++ b/WebKitTools/QtLauncher/mainwindow.cpp
@@ -39,10 +39,8 @@ MainWindow::MainWindow(const QString& url)
: m_page(new WebPage(this))
{
setAttribute(Qt::WA_DeleteOnClose);
-#if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0)
if (qgetenv("QTLAUNCHER_USE_ARGB_VISUALS").toInt() == 1)
setAttribute(Qt::WA_TranslucentBackground);
-#endif
buildUI();
}
@@ -73,6 +71,7 @@ void MainWindow::buildUI()
connect(page()->mainFrame(), SIGNAL(titleChanged(const QString&)),
this, SLOT(setWindowTitle(const QString&)));
+ connect(page()->mainFrame(), SIGNAL(urlChanged(QUrl)), this, SLOT(setAddressUrl(QUrl)));
connect(page(), SIGNAL(loadProgress(int)), urlEdit, SLOT(setProgress(int)));
connect(page(), SIGNAL(windowCloseRequested()), this, SLOT(close()));
@@ -97,6 +96,11 @@ WebPage* MainWindow::page()
return m_page;
}
+void MainWindow::setAddressUrl(const QUrl& url)
+{
+ urlEdit->setText(url.toString(QUrl::RemoveUserInfo));
+}
+
void MainWindow::setAddressUrl(const QString& url)
{
urlEdit->setText(url);
diff --git a/WebKitTools/QtLauncher/mainwindow.h b/WebKitTools/QtLauncher/mainwindow.h
index 1a30a09..d753a46 100644
--- a/WebKitTools/QtLauncher/mainwindow.h
+++ b/WebKitTools/QtLauncher/mainwindow.h
@@ -44,7 +44,6 @@ class MainWindow : public QMainWindow {
public:
MainWindow(const QString& url = QString());
- void setAddressUrl(const QString& url);
void addCompleterEntry(const QUrl& url);
void load(const QString& url);
@@ -53,6 +52,8 @@ public:
WebPage* page();
protected slots:
+ void setAddressUrl(const QString& url);
+ void setAddressUrl(const QUrl& url);
void openFile();
void changeLocation();
diff --git a/WebKitTools/QtLauncher/useragentlist.txt b/WebKitTools/QtLauncher/useragentlist.txt
new file mode 100644
index 0000000..ca53693
--- /dev/null
+++ b/WebKitTools/QtLauncher/useragentlist.txt
@@ -0,0 +1,9 @@
+Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/533.3 (KHTML, like Gecko) QtLauncher/0.1 Safari/533.3
+Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0; en-GB) AppleWebKit/533.3 (KHTML, like Gecko) QtLauncher/0.1 Mobile Safari/533.3
+Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8
+Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2
+Mozilla/5.0 (iPhone; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10
+Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10
+Opera/9.25 (Windows NT 6.0; U; en)
+Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1b/20.2.014; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413
+Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)
diff --git a/WebKitTools/QtLauncher/utils.h b/WebKitTools/QtLauncher/utils.h
index 9a16067..b67351e 100644
--- a/WebKitTools/QtLauncher/utils.h
+++ b/WebKitTools/QtLauncher/utils.h
@@ -28,10 +28,18 @@
#ifndef utils_h
#define utils_h
-#include <wtf/AlwaysInline.h>
-
#include <QtCore>
+#ifndef NO_RETURN
+#if defined(__CC_ARM) || defined(__ARMCC__)
+#define NO_RETURN __declspec(noreturn)
+#elif defined(__GNUC__)
+#define NO_RETURN __attribute((__noreturn__))
+#else
+#define NO_RETURN
+#endif
+#endif
+
// options handling
QString takeOptionValue(QStringList* arguments, int index);
QString formatKeys(QList<QString> keys);
diff --git a/WebKitTools/QtLauncher/webpage.cpp b/WebKitTools/QtLauncher/webpage.cpp
index 2fe1306..624a66f 100644
--- a/WebKitTools/QtLauncher/webpage.cpp
+++ b/WebKitTools/QtLauncher/webpage.cpp
@@ -39,6 +39,8 @@
WebPage::WebPage(QObject* parent)
: QWebPage(parent)
+ , m_userAgent()
+ , m_interruptingJavaScriptEnabled(false)
{
applyProxy();
}
@@ -103,4 +105,16 @@ void WebPage::openUrlInDefaultBrowser(const QUrl& url)
QDesktopServices::openUrl(url);
}
+QString WebPage::userAgentForUrl(const QUrl& url) const
+{
+ if (!m_userAgent.isEmpty())
+ return m_userAgent;
+ return QWebPage::userAgentForUrl(url);
+}
+bool WebPage::shouldInterruptJavaScript()
+{
+ if (!m_interruptingJavaScriptEnabled)
+ return false;
+ return QWebPage::shouldInterruptJavaScript();
+}
diff --git a/WebKitTools/QtLauncher/webpage.h b/WebKitTools/QtLauncher/webpage.h
index 14a0571..061deb5 100644
--- a/WebKitTools/QtLauncher/webpage.h
+++ b/WebKitTools/QtLauncher/webpage.h
@@ -49,11 +49,18 @@ public:
virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type);
+ QString userAgentForUrl(const QUrl& url) const;
+ void setInterruptingJavaScriptEnabled(bool enabled) { m_interruptingJavaScriptEnabled = enabled; }
+
public slots:
void openUrlInDefaultBrowser(const QUrl& url = QUrl());
+ void setUserAgent(const QString& ua) { m_userAgent = ua; }
+ bool shouldInterruptJavaScript();
private:
void applyProxy();
+ QString m_userAgent;
+ bool m_interruptingJavaScriptEnabled;
};
#endif
diff --git a/WebKitTools/QtLauncher/webview.cpp b/WebKitTools/QtLauncher/webview.cpp
index 311d79b..8c94438 100644
--- a/WebKitTools/QtLauncher/webview.cpp
+++ b/WebKitTools/QtLauncher/webview.cpp
@@ -41,10 +41,12 @@ WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
, m_numPaintsTotal(0)
, m_numPaintsSinceLastMeasure(0)
, m_measureFps(false)
+ , m_resizesToContents(false)
{
setScene(new QGraphicsScene(this));
scene()->addItem(m_item);
+ setFrameShape(QFrame::NoFrame);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -71,23 +73,42 @@ WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
machine->setInitialState(s0);
machine->start();
#endif
+
+ m_updateTimer = new QTimer(this);
+ m_updateTimer->setInterval(1000);
+ connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateFrameRate()));
+}
+
+void WebViewGraphicsBased::setResizesToContents(bool b)
+{
+ m_resizesToContents = b;
+ m_item->setResizesToContents(m_resizesToContents);
+ if (m_resizesToContents) {
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ } else {
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
}
void WebViewGraphicsBased::resizeEvent(QResizeEvent* event)
{
QGraphicsView::resizeEvent(event);
+ if (m_resizesToContents)
+ return;
QRectF rect(QPoint(0, 0), event->size());
m_item->setGeometry(rect);
}
-void WebViewGraphicsBased::enableFrameRateMeasurement()
+void WebViewGraphicsBased::setFrameRateMeasurementEnabled(bool enabled)
{
- m_measureFps = true;
- m_lastConsultTime = m_startTime = QTime::currentTime();
- QTimer* updateTimer = new QTimer(this);
- updateTimer->setInterval(1000);
- connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateFrameRate()));
- updateTimer->start();
+ m_measureFps = enabled;
+ if (m_measureFps) {
+ m_lastConsultTime = m_startTime = QTime::currentTime();
+ m_updateTimer->start();
+ } else
+ m_updateTimer->stop();
}
void WebViewGraphicsBased::updateFrameRate()
@@ -100,7 +121,7 @@ void WebViewGraphicsBased::updateFrameRate()
int average = total ? m_numPaintsTotal * 1000 / total : 0;
int current = interval ? m_numPaintsSinceLastMeasure * 1000 / interval : 0;
- qDebug("[FPS] average: %d, current: %d", average, current);
+ emit currentFPSUpdated(current);
m_lastConsultTime = now;
m_numPaintsSinceLastMeasure = 0;
diff --git a/WebKitTools/QtLauncher/webview.h b/WebKitTools/QtLauncher/webview.h
index 297d975..e8c3226 100644
--- a/WebKitTools/QtLauncher/webview.h
+++ b/WebKitTools/QtLauncher/webview.h
@@ -73,10 +73,15 @@ public:
virtual void resizeEvent(QResizeEvent*);
void setPage(QWebPage* page) { m_item->setPage(page); }
void setItemCacheMode(QGraphicsItem::CacheMode mode) { m_item->setCacheMode(mode); }
+ QGraphicsItem::CacheMode itemCacheMode() { return m_item->cacheMode(); }
+
+ void setFrameRateMeasurementEnabled(bool enabled);
+ bool frameRateMeasurementEnabled() const { return m_measureFps; }
- void enableFrameRateMeasurement();
virtual void paintEvent(QPaintEvent* event);
+ void setResizesToContents(bool b);
+
void setYRotation(qreal angle)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
@@ -93,6 +98,8 @@ public:
return m_yRotation;
}
+ GraphicsWebView* graphicsWebView() const { return m_item; }
+
public slots:
void updateFrameRate();
void animatedFlip();
@@ -100,6 +107,7 @@ public slots:
signals:
void yFlipRequest();
+ void currentFPSUpdated(int fps);
private:
GraphicsWebView* m_item;
@@ -107,8 +115,10 @@ private:
int m_numPaintsSinceLastMeasure;
QTime m_startTime;
QTime m_lastConsultTime;
+ QTimer* m_updateTimer;
bool m_measureFps;
qreal m_yRotation;
+ bool m_resizesToContents;
};
#endif
diff --git a/WebKitTools/QueueStatusServer/handlers/__init__.py b/WebKitTools/QueueStatusServer/handlers/__init__.py
index ef65bee..296e173 100644
--- a/WebKitTools/QueueStatusServer/handlers/__init__.py
+++ b/WebKitTools/QueueStatusServer/handlers/__init__.py
@@ -1 +1,3 @@
# Required for Python to search this directory for module files
+
+from handlers.updatebase import UpdateBase
diff --git a/WebKitTools/QueueStatusServer/handlers/svnrevision.py b/WebKitTools/QueueStatusServer/handlers/svnrevision.py
new file mode 100644
index 0000000..36eab33
--- /dev/null
+++ b/WebKitTools/QueueStatusServer/handlers/svnrevision.py
@@ -0,0 +1,40 @@
+# Copyright (C) 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 google.appengine.ext import webapp
+
+import model
+
+
+class SVNRevision(webapp.RequestHandler):
+ def get(self, svn_revision_number):
+ svn_revisions = model.SVNRevision.all().filter('number =', int(svn_revision_number)).order('-date').fetch(1)
+ if not svn_revisions:
+ self.error(404)
+ return
+ self.response.out.write(svn_revisions[0].to_xml())
diff --git a/WebKitTools/QueueStatusServer/handlers/updatebase.py b/WebKitTools/QueueStatusServer/handlers/updatebase.py
new file mode 100644
index 0000000..b087e83
--- /dev/null
+++ b/WebKitTools/QueueStatusServer/handlers/updatebase.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2010 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 google.appengine.api import users
+from google.appengine.ext import webapp, db
+
+
+class UpdateBase(webapp.RequestHandler):
+ def _int_from_request(self, name):
+ string_value = self.request.get(name)
+ try:
+ int_value = int(string_value)
+ return int_value
+ except ValueError, TypeError:
+ pass
+ return None
diff --git a/WebKitTools/QueueStatusServer/handlers/updatestatus.py b/WebKitTools/QueueStatusServer/handlers/updatestatus.py
index 3ad7b77..50d4b6e 100644
--- a/WebKitTools/QueueStatusServer/handlers/updatestatus.py
+++ b/WebKitTools/QueueStatusServer/handlers/updatestatus.py
@@ -30,23 +30,14 @@ from google.appengine.api import users
from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import template
+from handlers.updatebase import UpdateBase
from model.attachment import Attachment
from model.queuestatus import QueueStatus
-
-class UpdateStatus(webapp.RequestHandler):
+class UpdateStatus(UpdateBase):
def get(self):
self.response.out.write(template.render("templates/updatestatus.html", None))
- def _int_from_request(self, name):
- string_value = self.request.get(name)
- try:
- int_value = int(string_value)
- return int_value
- except ValueError, TypeError:
- pass
- return None
-
def post(self):
queue_status = QueueStatus()
diff --git a/WebKitTools/QueueStatusServer/handlers/updatesvnrevision.py b/WebKitTools/QueueStatusServer/handlers/updatesvnrevision.py
new file mode 100644
index 0000000..075982a
--- /dev/null
+++ b/WebKitTools/QueueStatusServer/handlers/updatesvnrevision.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2010 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 google.appengine.ext import webapp, db
+from google.appengine.ext.webapp import template
+
+import handlers
+import model
+
+
+class UpdateSVNRevision(handlers.UpdateBase):
+ def get(self):
+ self.response.out.write(template.render("templates/updatesvnrevision.html", None))
+
+ def post(self):
+ svn_revision_number = self._int_from_request("number")
+
+ svn_revisions = model.SVNRevision.all().filter('number =', svn_revision_number).order('-date').fetch(1)
+ svn_revision = None
+ if svn_revisions:
+ svn_revision = svn_revisions[0]
+ else:
+ svn_revision = model.SVNRevision()
+ svn_revision.number = svn_revision_number
+ svn_revision.broken_bots.append(self.request.get("broken_bot"))
+ svn_revision.put()
+
+ self.response.out.write(svn_revision.key().id())
diff --git a/WebKitTools/QueueStatusServer/index.yaml b/WebKitTools/QueueStatusServer/index.yaml
index 60ba3df..94b6692 100644
--- a/WebKitTools/QueueStatusServer/index.yaml
+++ b/WebKitTools/QueueStatusServer/index.yaml
@@ -28,3 +28,9 @@ indexes:
- name: queue_name
- name: date
direction: desc
+
+- kind: SVNRevision
+ properties:
+ - name: number
+ - name: date
+ direction: desc
diff --git a/WebKitTools/QueueStatusServer/main.py b/WebKitTools/QueueStatusServer/main.py
index 8efc771..8bfa7e9 100644
--- a/WebKitTools/QueueStatusServer/main.py
+++ b/WebKitTools/QueueStatusServer/main.py
@@ -40,7 +40,9 @@ from handlers.patchstatus import PatchStatus
from handlers.recentstatus import RecentStatus
from handlers.showresults import ShowResults
from handlers.statusbubble import StatusBubble
+from handlers.svnrevision import SVNRevision
from handlers.updatestatus import UpdateStatus
+from handlers.updatesvnrevision import UpdateSVNRevision
webapp.template.register_template_library('filters.webkit_extras')
@@ -52,8 +54,10 @@ routes = [
(r'/patch/(.*)', Patch),
(r'/results/(.*)', ShowResults),
(r'/status-bubble/(.*)', StatusBubble),
+ (r'/svn-revision/(.*)', SVNRevision),
(r'/queue-status/(.*)', RecentStatus),
('/update-status', UpdateStatus),
+ ('/update-svn-revision', UpdateSVNRevision),
]
application = webapp.WSGIApplication(routes, debug=True)
diff --git a/WebKitTools/QueueStatusServer/model/__init__.py b/WebKitTools/QueueStatusServer/model/__init__.py
index ef65bee..1eaa044 100644
--- a/WebKitTools/QueueStatusServer/model/__init__.py
+++ b/WebKitTools/QueueStatusServer/model/__init__.py
@@ -1 +1,3 @@
# Required for Python to search this directory for module files
+
+from model.svnrevision import SVNRevision
diff --git a/WebKitTools/QueueStatusServer/model/queues.py b/WebKitTools/QueueStatusServer/model/queues.py
index 8d48aff..57463de 100644
--- a/WebKitTools/QueueStatusServer/model/queues.py
+++ b/WebKitTools/QueueStatusServer/model/queues.py
@@ -27,10 +27,12 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
queues = [
+ "commit-queue",
"style-queue",
"chromium-ews",
+ "cr-win-ews",
"qt-ews",
- "mac-ews",
"gtk-ews",
- "commit-queue",
+ "mac-ews",
+ "win-ews",
]
diff --git a/WebKitTools/QueueStatusServer/model/svnrevision.py b/WebKitTools/QueueStatusServer/model/svnrevision.py
new file mode 100644
index 0000000..70ec0cc
--- /dev/null
+++ b/WebKitTools/QueueStatusServer/model/svnrevision.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2010 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 google.appengine.ext import db
+
+class SVNRevision(db.Model):
+ number = db.IntegerProperty()
+ broken_bots = db.StringListProperty(default=[])
+ date = db.DateTimeProperty(auto_now_add=True)
diff --git a/WebKitTools/QueueStatusServer/templates/dashboard.html b/WebKitTools/QueueStatusServer/templates/dashboard.html
index 84ecabb..14b7ede 100644
--- a/WebKitTools/QueueStatusServer/templates/dashboard.html
+++ b/WebKitTools/QueueStatusServer/templates/dashboard.html
@@ -18,9 +18,11 @@ function statusDetail(patch_id) {
<th>Bug</th>
<th>Attachment</th>
<th>Style</th>
- <th>Chromium</th>
+ <th>Cr-Linux</th>
+ <th>Cr-Win</th>
<th>Qt</th>
<th>Mac</th>
+ <th>Win</th>
<th>Gtk</th>
<th>Commit</th>
</tr>
@@ -42,6 +44,10 @@ function statusDetail(patch_id) {
onclick="statusDetail({{ summary.attachment_id }})"
title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}>
</td>
+ <td class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %}
+ onclick="statusDetail({{ summary.attachment_id }})"
+ title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}>
+ </td>
<td class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %}
onclick="statusDetail({{ summary.attachment_id }})"
title="{{ summary.qt_ews.status.date|timesince }} ago"{% endif %}>
@@ -50,6 +56,10 @@ function statusDetail(patch_id) {
onclick="statusDetail({{ summary.attachment_id }})"
title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}>
</td>
+ <td class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %}
+ onclick="statusDetail({{ summary.attachment_id }})"
+ title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}>
+ </td>
<td class="status {{ summary.gtk_ews.state }}"{% if summary.gtk_ews.status %}
onclick="statusDetail({{ summary.attachment_id }})"
title="{{ summary.gtk_ews.status.date|timesince }} ago"{% endif %}>
diff --git a/WebKitTools/QueueStatusServer/templates/statusbubble.html b/WebKitTools/QueueStatusServer/templates/statusbubble.html
index d1f331c..c6e2134 100644
--- a/WebKitTools/QueueStatusServer/templates/statusbubble.html
+++ b/WebKitTools/QueueStatusServer/templates/statusbubble.html
@@ -56,7 +56,12 @@ function statusDetail(patch_id) {
<div class="status {{ summary.chromium_ews.state }}"{% if summary.chromium_ews.status %}
onclick="statusDetail({{ summary.attachment_id }})"
title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}>
- chromium
+ cr-linux
+</div>
+<div class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %}
+ onclick="statusDetail({{ summary.attachment_id }})"
+ title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}>
+ cr-win
</div>
<div class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %}
onclick="statusDetail({{ summary.attachment_id }})"
@@ -73,5 +78,10 @@ function statusDetail(patch_id) {
title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}>
mac
</div>
+<div class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %}
+ onclick="statusDetail({{ summary.attachment_id }})"
+ title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}>
+ win
+</div>
</body>
</html>
diff --git a/WebKitTools/QueueStatusServer/templates/updatesvnrevision.html b/WebKitTools/QueueStatusServer/templates/updatesvnrevision.html
new file mode 100644
index 0000000..6ea4e7b
--- /dev/null
+++ b/WebKitTools/QueueStatusServer/templates/updatesvnrevision.html
@@ -0,0 +1,8 @@
+<form name="update_svn_revision" method="post">
+Update an SVN revision: <input name="number">
+ <div>
+ Broken Bot:
+ <input name="broken_bot">
+ </div>
+ <div><input type="submit" value="Update"></div>
+</form>
diff --git a/WebKitTools/Scripts/VCSUtils.pm b/WebKitTools/Scripts/VCSUtils.pm
index 022c72a..25a319b 100644
--- a/WebKitTools/Scripts/VCSUtils.pm
+++ b/WebKitTools/Scripts/VCSUtils.pm
@@ -61,6 +61,7 @@ BEGIN {
&isSVNDirectory
&isSVNVersion16OrNewer
&makeFilePathRelative
+ &mergeChangeLogs
&normalizePath
&parsePatch
&pathRelativeToSVNRepositoryRootForPath
@@ -815,6 +816,76 @@ sub runPatchCommand($$$;$)
return $exitStatus;
}
+# Merge ChangeLog patches using a three-file approach.
+#
+# This is used by resolve-ChangeLogs when it's operated as a merge driver
+# and when it's used to merge conflicts after a patch is applied or after
+# an svn update.
+#
+# It's also used for traditional rejected patches.
+#
+# Args:
+# $fileMine: The merged version of the file. Also known in git as the
+# other branch's version (%B) or "ours".
+# For traditional patch rejects, this is the *.rej file.
+# $fileOlder: The base version of the file. Also known in git as the
+# ancestor version (%O) or "base".
+# For traditional patch rejects, this is the *.orig file.
+# $fileNewer: The current version of the file. Also known in git as the
+# current version (%A) or "theirs".
+# For traditional patch rejects, this is the original-named
+# file.
+#
+# Returns 1 if merge was successful, else 0.
+sub mergeChangeLogs($$$)
+{
+ my ($fileMine, $fileOlder, $fileNewer) = @_;
+
+ my $traditionalReject = $fileMine =~ /\.rej$/ ? 1 : 0;
+
+ local $/ = undef;
+
+ my $patch;
+ if ($traditionalReject) {
+ open(DIFF, "<", $fileMine) or die $!;
+ $patch = <DIFF>;
+ close(DIFF);
+ rename($fileMine, "$fileMine.save");
+ rename($fileOlder, "$fileOlder.save");
+ } else {
+ open(DIFF, "-|", qw(diff -u -a --binary), $fileOlder, $fileMine) or die $!;
+ $patch = <DIFF>;
+ close(DIFF);
+ }
+
+ unlink("${fileNewer}.orig");
+ unlink("${fileNewer}.rej");
+
+ open(PATCH, "| patch --force --fuzz=3 --binary $fileNewer > " . File::Spec->devnull()) or die $!;
+ print PATCH ($traditionalReject ? $patch : fixChangeLogPatch($patch));
+ close(PATCH);
+
+ my $result = !exitStatus($?);
+
+ # Refuse to merge the patch if it did not apply cleanly
+ if (-e "${fileNewer}.rej") {
+ unlink("${fileNewer}.rej");
+ if (-f "${fileNewer}.orig") {
+ unlink($fileNewer);
+ rename("${fileNewer}.orig", $fileNewer);
+ }
+ } else {
+ unlink("${fileNewer}.orig");
+ }
+
+ if ($traditionalReject) {
+ rename("$fileMine.save", $fileMine);
+ rename("$fileOlder.save", $fileOlder);
+ }
+
+ return $result;
+}
+
sub gitConfig($)
{
return unless $isGit;
diff --git a/WebKitTools/Scripts/build-dumprendertree b/WebKitTools/Scripts/build-dumprendertree
index 72e81b0..14690a8 100755
--- a/WebKitTools/Scripts/build-dumprendertree
+++ b/WebKitTools/Scripts/build-dumprendertree
@@ -45,6 +45,7 @@ Usage: $programName [options] [options to pass to build system]
--gtk Build the GTK+ port
--qt Build the Qt port
--wx Build the wxWindows port
+ --chromium Build the Chromium port
EOF
GetOptions(
@@ -72,6 +73,17 @@ if (isAppleMacWebKit()) {
} elsif (isQt() || isGtk() || isWx()) {
# Qt, Gtk and wxWindows build everything in one shot. No need to build anything here.
$result = 0;
+} elsif (isChromium()) {
+ if (isDarwin()) {
+ $result = buildXCodeProject("DumpRenderTree.gyp/DumpRenderTree", $clean, @ARGV);
+ } elsif (isCygwin() || isWindows()) {
+ # Windows build - builds the root visual studio solution.
+ $result = buildChromiumVisualStudioProject("DumpRenderTree.gyp/DumpRenderTree.sln", $clean);
+ } elsif (isLinux()) {
+ $result = buildChromiumMakefile("../../WebKit/chromium/", "DumpRenderTree", $clean);
+ } else {
+ die "This platform is not supported by Chromium.\n";
+ }
} else {
die "Building not defined for this platform!\n";
}
diff --git a/WebKitTools/Scripts/build-webkit b/WebKitTools/Scripts/build-webkit
index 5ae1aae..2d172c5 100755
--- a/WebKitTools/Scripts/build-webkit
+++ b/WebKitTools/Scripts/build-webkit
@@ -48,15 +48,17 @@ chdirWebKit();
my $showHelp = 0;
my $clean = 0;
my $minimal = 0;
+my $webkit2 = 0;
my $makeArgs;
my $startTime = time();
my ($threeDCanvasSupport, $threeDRenderingSupport, $channelMessagingSupport, $clientBasedGeolocationSupport, $databaseSupport, $datagridSupport, $datalistSupport,
$domStorageSupport, $eventsourceSupport, $filtersSupport, $geolocationSupport, $iconDatabaseSupport, $indexedDatabaseSupport,
- $javaScriptDebuggerSupport, $mathmlSupport, $offlineWebApplicationSupport, $rubySupport, $sharedWorkersSupport,
+ $javaScriptDebuggerSupport, $mathmlSupport, $offlineWebApplicationSupport, $rubySupport, $systemMallocSupport, $sandboxSupport, $sharedWorkersSupport,
$svgSupport, $svgAnimationSupport, $svgAsImageSupport, $svgDOMObjCBindingsSupport, $svgFontsSupport,
$svgForeignObjectSupport, $svgUseSupport, $videoSupport, $webSocketsSupport, $wmlSupport, $wcssSupport, $xhtmlmpSupport, $workersSupport,
- $xpathSupport, $xsltSupport, $coverageSupport, $notificationsSupport);
+ $xpathSupport, $xsltSupport, $coverageSupport, $notificationsSupport, $blobSliceSupport, $tiledBackingStoreSupport,
+ $fileReaderSupport, $fileWriterSupport);
my @features = (
{ option => "3d-canvas", desc => "Toggle 3D canvas support",
@@ -65,6 +67,9 @@ my @features = (
{ option => "3d-rendering", desc => "Toggle 3D rendering support",
define => "ENABLE_3D_RENDERING", default => (isAppleMacWebKit() && !isTiger()), value => \$threeDRenderingSupport },
+ { option => "blob-slice", desc => "Toggle Blob.slice support",
+ define => "ENABLE_BLOB_SLICE", default => (isAppleMacWebKit()), value => \$blobSliceSupport },
+
{ option => "channel-messaging", desc => "Toggle MessageChannel and MessagePort support",
define => "ENABLE_CHANNEL_MESSAGING", default => 1, value => \$channelMessagingSupport },
@@ -116,6 +121,12 @@ my @features = (
{ option => "ruby", desc => "Toggle HTML5 Ruby support",
define => "ENABLE_RUBY", default => 1, value => \$rubySupport },
+ { option => "system-malloc", desc => "Toggle system allocator instead of TCmalloc",
+ define => "USE_SYSTEM_MALLOC", default => 0, value => \$systemMallocSupport },
+
+ { option => "sandbox", desc => "Toggle HTML5 Sandboxed iframe support",
+ define => "ENABLE_SANDBOX", default => 1, value => \$sandboxSupport },
+
{ option => "shared-workers", desc => "Toggle SharedWorkers support",
define => "ENABLE_SHARED_WORKERS", default => (isAppleWebKit() || isGtk()), value => \$sharedWorkersSupport },
@@ -140,6 +151,9 @@ my @features = (
{ option => "svg-use", desc => "Toggle SVG use element support (implies SVG support)",
define => "ENABLE_SVG_USE", default => 1, value => \$svgUseSupport },
+ { option => "tiled-backing-store", desc => "Toggle Tiled Backing Store support",
+ define => "ENABLE_TILED_BACKING_STORE", default => isQt(), value => \$tiledBackingStoreSupport },
+
{ option => "video", desc => "Toggle Video support",
define => "ENABLE_VIDEO", default => (isAppleWebKit() || isGtk()), value => \$videoSupport },
@@ -163,6 +177,12 @@ my @features = (
{ option => "xslt", desc => "Toggle XSLT support",
define => "ENABLE_XSLT", default => 1, value => \$xsltSupport },
+
+ { option => "file-reader", desc => "Toggle FileReader support",
+ define => "ENABLE_FILE_READER", default => 0, value => \$fileReaderSupport },
+
+ { option => "file-writer", desc => "Toggle FileWriter support",
+ define => "ENABLE_FILE_WRITER", default => 0, value => \$fileWriterSupport },
);
# Update defaults from Qt's project file
@@ -204,6 +224,7 @@ Usage: $programName [options] [options to pass to build system]
--chromium Build the Chromium port on Mac/Win/Linux
--gtk Build the GTK+ port
--qt Build the Qt port
+ --webkit2 Build the WebKit2 framework
--inspector-frontend Copy changes to the inspector front-end files to the build directory
--makeargs=<arguments> Optional Makefile flags
@@ -217,6 +238,7 @@ my %options = (
'clean' => \$clean,
'makeargs=s' => \$makeArgs,
'minimal' => \$minimal,
+ 'webkit2' => \$webkit2,
);
# Build usage text and options list from features
@@ -239,7 +261,14 @@ setConfiguration();
my $productDir = productDir();
# Check that all the project directories are there.
-my @projects = ("JavaScriptCore", "WebCore", "WebKit");
+my @projects = ("JavaScriptCore", "WebCore");
+
+if (!$webkit2) {
+ push @projects, "WebKit";
+} else {
+ push @projects, ("WebKit2", "WebKitTools/MiniBrowser");
+}
+
# Only Apple builds JavaScriptGlue, and only on the Mac
splice @projects, 1, 0, "JavaScriptGlue" if isAppleMacWebKit();
@@ -254,6 +283,7 @@ my @options = ();
# enable autotool options accordingly
if (isGtk()) {
+ @options = @ARGV;
foreach (@features) {
push @options, autotoolsFlag(${$_->{value}}, $_->{option});
}
@@ -359,7 +389,18 @@ for my $dir (@projects) {
} elsif (isQt()) {
$result = buildQMakeQtProject($dir, $clean, @options);
} elsif (isAppleMacWebKit()) {
- $result = buildXCodeProject($dir, $clean, @options, @ARGV);
+ my @completeOptions = @options;
+ if ($webkit2 && $dir eq "WebCore") {
+ my @webKit2SpecificOverrides = (
+ 'UMBRELLA_LDFLAGS=',
+ 'GCC_PREPROCESSOR_DEFINITIONS=$(GCC_PREPROCESSOR_DEFINITIONS) ' .
+ 'ENABLE_EXPERIMENTAL_SINGLE_VIEW_MODE=1 ' .
+ 'WTF_USE_WEB_THREAD=1 '
+ );
+ push @completeOptions, @webKit2SpecificOverrides;
+ }
+
+ $result = buildXCodeProject($dir, $clean, @completeOptions, @ARGV);
} elsif (isAppleWinWebKit()) {
if ($dir eq "WebKit") {
$result = buildVisualStudioProject("win/WebKit.vcproj/WebKit.sln", $clean);
@@ -420,10 +461,16 @@ sub writeCongrats()
print "\n";
print "===========================================================\n";
- print " WebKit is now built ($buildTime). \n";
- if (!isChromium()) {
- print " To run $launcherName with this newly-built code, use the\n";
- print " \"$launcherPath\" script.\n";
+ if ($webkit2) {
+ print " WebKit2 is now built ($buildTime). \n";
+ print " To run MiniBrowser with this newly-built code, use the\n";
+ print " \"run-minibrowser\" script.\n";
+ } else {
+ print " WebKit is now built ($buildTime). \n";
+ if (!isChromium()) {
+ print " To run $launcherName with this newly-built code, use the\n";
+ print " \"$launcherPath\" script.\n";
+ }
}
print "===========================================================\n";
}
diff --git a/WebKitTools/Scripts/check-for-global-initializers b/WebKitTools/Scripts/check-for-global-initializers
index cf83048..0472901 100755
--- a/WebKitTools/Scripts/check-for-global-initializers
+++ b/WebKitTools/Scripts/check-for-global-initializers
@@ -37,6 +37,7 @@ use strict;
use File::Basename;
sub touch($);
+sub demangle($);
my $arch = $ENV{'CURRENT_ARCH'};
my $configuration = $ENV{'CONFIGURATION'};
@@ -78,9 +79,14 @@ for my $file (sort @files) {
next;
}
my $sawGlobal = 0;
+ my @globals;
while (<NM>) {
if (/^STDOUT:/) {
- $sawGlobal = 1 if /__GLOBAL__I/;
+ my $line = $_;
+ if ($line =~ /__GLOBAL__I(.+)$/) {
+ $sawGlobal = 1;
+ push(@globals, demangle($1));
+ }
} else {
print STDERR if $_ ne "nm: no name list\n";
}
@@ -119,7 +125,7 @@ for my $file (sort @files) {
}
}
- print "ERROR: $shortName has a global initializer in it! ($file)\n";
+ print "ERROR: $shortName has one or more global initializers in it! ($file), near @globals\n";
$sawError = 1;
}
}
@@ -138,3 +144,17 @@ sub touch($)
open(TOUCH, ">", $path) or die "$!";
close(TOUCH);
}
+
+sub demangle($)
+{
+ my ($symbol) = @_;
+ if (!open FILT, "c++filt $symbol |") {
+ print "ERROR: Could not open c++filt\n";
+ return;
+ }
+ my $result = <FILT>;
+ close FILT;
+ chomp $result;
+ return $result;
+}
+
diff --git a/WebKitTools/Scripts/check-for-inappropriate-files-in-framework b/WebKitTools/Scripts/check-for-inappropriate-files-in-framework
new file mode 100755
index 0000000..2bc65d4
--- /dev/null
+++ b/WebKitTools/Scripts/check-for-inappropriate-files-in-framework
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2010 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 INC. 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 INC. 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.
+
+base_directory = ENV['TARGET_BUILD_DIR'] or throw "Unable to find TARGET_BUILD_DIR in the environment!"
+project_name = ENV['PROJECT_NAME'] or throw "Unable to find PROJECT_NAME in the environment!"
+
+$INAPPROPRIATE_FILES = { "WebCore" => { "Resources" => ["*.css", "*.in", "*.idl"] } }
+
+Dir.chdir base_directory
+
+$error_printed = false
+
+def print_error msg
+ $error_printed = true
+ STDERR.puts "ERROR: #{msg}"
+end
+
+def print_inappropriate_file_error framework, relative_path
+ print_error "#{framework}.framework/#{relative_path} should not be present in the framework."
+end
+
+def check_framework framework
+ $INAPPROPRIATE_FILES[framework].each do |directory, patterns|
+ Dir.chdir "#{framework}.framework/Versions/A/#{directory}" do
+ patterns.each do |pattern|
+ Dir.glob(pattern).each do |inappropriate_file|
+ print_inappropriate_file_error framework, "Resources/#{inappropriate_file}"
+ File.unlink inappropriate_file
+ end
+ end
+ end
+ end
+end
+
+check_framework project_name
+
+if $error_printed
+ STDERR.puts
+ STDERR.puts " Inappropriate files were detected and have been removed from the framework."
+ STDERR.puts " If this error continues to appear after building again then the build system needs"
+ STDERR.puts " to be modified so that the inappropriate files are no longer copied in to the framework."
+ STDERR.puts
+ exit 1
+end
diff --git a/WebKitTools/Scripts/check-for-webkit-framework-include-consistency b/WebKitTools/Scripts/check-for-webkit-framework-include-consistency
new file mode 100755
index 0000000..693dd6a
--- /dev/null
+++ b/WebKitTools/Scripts/check-for-webkit-framework-include-consistency
@@ -0,0 +1,109 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2010 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 INC. 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 INC. 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.
+
+
+base_directory = ENV['TARGET_BUILD_DIR']
+
+unless base_directory
+ throw "Unable to find TARGET_BUILD_DIR in the environment!"
+end
+
+Dir.chdir base_directory
+
+$PERMITTED_INCLUDE_TYPES = { :public => [ :public ], :private => [ :public, :private ] }
+
+$HEADER_NAMES_TO_TYPE = { }
+$HEADERS_BY_TYPE = { :public => [], :private => [] }
+
+$error_printed = false
+
+def print_error msg
+ $error_printed = true
+ STDERR.puts "ERROR: #{msg}"
+end
+
+def build_header_maps
+ all_headers = `find WebKit.framework/Versions/A/{,Private}Headers -type f -name '*.h'`.split
+
+ all_headers.each do |header|
+ if /\/Headers\/(.*)/.match(header)
+ $HEADER_NAMES_TO_TYPE[$1] = :public
+ $HEADERS_BY_TYPE[:public] << header
+ elsif /\/PrivateHeaders\/(.*)/.match(header)
+ $HEADER_NAMES_TO_TYPE[$1] = :private
+ $HEADERS_BY_TYPE[:private] << header
+ else
+ print_error "Unknown header type: #{header}"
+ end
+ end
+end
+
+def resolve_include(header, included_header, permitted_types)
+ # Ignore includes that aren't in the typical framework style.
+ return unless /<([^\/]+)\/(.*)>/.match(included_header)
+
+ framework, included_header_name = [$1, $2]
+
+ # Ignore includes that aren't related to other WebKit headers.
+ return unless framework =~ /^Web/
+
+ # A header of any type including a WebCore header is a recipe for disaster.
+ if framework == "WebCore"
+ # <rdar://problem/7718826> WebKeyGenerator.h should not include a WebCore header
+ return if header =~ /\/WebKeyGenerator.h$/ and included_header_name == "WebCoreKeyGenerator.h"
+
+ print_error "#{header} included #{included_header}!"
+ return
+ end
+
+ header_type = $HEADER_NAMES_TO_TYPE[included_header_name]
+
+ if not header_type
+ print_error "#{header} included #{included_header} but I could not find a header of that name!"
+ elsif not permitted_types.member?(header_type)
+ print_error "#{header} included #{included_header} which is #{header_type}!"
+ end
+end
+
+def verify_includes(header, permitted_types)
+ File.open(header) do |file|
+ file.each_line do |line|
+ if /#(include|import) (.*)/.match(line)
+ resolve_include(header, $2, permitted_types)
+ end
+ end
+ end
+end
+
+build_header_maps
+
+$HEADERS_BY_TYPE.each do |header_type, headers|
+ permitted_types = $PERMITTED_INCLUDE_TYPES[header_type]
+ headers.each do |header|
+ verify_includes header, permitted_types
+ end
+end
+
+exit 1 if $error_printed
diff --git a/WebKitTools/Scripts/check-webkit-style b/WebKitTools/Scripts/check-webkit-style
index ea2e943..9897fbd 100755
--- a/WebKitTools/Scripts/check-webkit-style
+++ b/WebKitTools/Scripts/check-webkit-style
@@ -43,51 +43,84 @@ same line, but it is far from perfect (in either direction).
"""
import codecs
+import logging
import os
import os.path
import sys
+from webkitpy.style_references import detect_checkout
import webkitpy.style.checker as checker
-from webkitpy.style_references import SimpleScm
+from webkitpy.style.checker import PatchChecker
+from webkitpy.style.main import change_directory
+_log = logging.getLogger("check-webkit-style")
+
+
+# FIXME: Move this code to style.main.
def main():
# Change stderr to write with replacement characters so we don't die
# if we try to print something containing non-ASCII characters.
- sys.stderr = codecs.StreamReaderWriter(sys.stderr,
- codecs.getreader('utf8'),
- codecs.getwriter('utf8'),
- 'replace')
+ stderr = codecs.StreamReaderWriter(sys.stderr,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+ # Setting an "encoding" attribute on the stream is necessary to
+ # prevent the logging module from raising an error. See
+ # the checker.configure_logging() function for more information.
+ stderr.encoding = "UTF-8"
+
+ # FIXME: Change webkitpy.style so that we do not need to overwrite
+ # the global sys.stderr. This involves updating the code to
+ # accept a stream parameter where necessary, and not calling
+ # sys.stderr explicitly anywhere.
+ sys.stderr = stderr
+
+ args = sys.argv[1:]
+
+ # Checking for the verbose flag before calling check_webkit_style_parser()
+ # lets us enable verbose logging earlier.
+ is_verbose = "-v" in args or "--verbose" in args
+
+ checker.configure_logging(stream=stderr, is_verbose=is_verbose)
+ _log.debug("Verbose logging enabled.")
+
+ checkout = detect_checkout()
+
+ if checkout is None:
+ checkout_root = None
+ _log.debug("WebKit checkout not found for current directory.")
+ else:
+ checkout_root = checkout.root_path()
+ _log.debug("WebKit checkout found with root: %s" % checkout_root)
+
parser = checker.check_webkit_style_parser()
- (files, options) = parser.parse(sys.argv[1:])
+ (paths, options) = parser.parse(args)
+
+ if checkout is None and not paths:
+ _log.error("WebKit checkout not found: You must run this script "
+ "from within a WebKit checkout if you are not passing "
+ "specific paths to check.")
+ sys.exit(1)
configuration = checker.check_webkit_style_configuration(options)
style_checker = checker.StyleChecker(configuration)
- if files:
- for filename in files:
- style_checker.check_file(filename)
+ paths = change_directory(checkout_root=checkout_root, paths=paths)
+ if paths:
+ style_checker.check_paths(paths)
else:
- scm = SimpleScm()
-
- os.chdir(scm.checkout_root())
-
if options.git_commit:
- commit = options.git_commit
- if '..' in commit:
- # FIXME: If the range is a "...", the code should find the common ancestor and
- # start there (see git diff --help for information about how ... usually works).
- commit = commit[:commit.find('..')]
- print >> sys.stderr, "Warning: Ranges are not supported for --git-commit. Checking all changes since %s.\n" % commit
- patch = scm.create_patch_since_local_commit(commit)
+ patch = checkout.create_patch_since_local_commit(options.git_commit)
else:
- patch = scm.create_patch()
- style_checker.check_patch(patch)
+ patch = checkout.create_patch()
+ patch_checker = PatchChecker(style_checker)
+ patch_checker.check(patch)
error_count = style_checker.error_count
file_count = style_checker.file_count
- sys.stderr.write('Total errors found: %d in %d files\n'
- % (error_count, file_count))
+ _log.info("Total errors found: %d in %d files"
+ % (error_count, file_count))
# We fail when style errors are found or there are no checked files.
sys.exit(error_count > 0 or file_count == 0)
diff --git a/WebKitTools/Scripts/commit-log-editor b/WebKitTools/Scripts/commit-log-editor
index 75017e3..a642731 100755
--- a/WebKitTools/Scripts/commit-log-editor
+++ b/WebKitTools/Scripts/commit-log-editor
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
-# Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
# Copyright (C) 2009 Torch Mobile Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -40,6 +40,7 @@ use webkitdirs;
sub normalizeLineEndings($$);
sub removeLongestCommonPrefixEndingInDoubleNewline(\%);
+sub isCommitLogEditor($);
sub usage
{
@@ -61,30 +62,33 @@ if (!$log) {
my $baseDir = baseProductDir();
my $editor = $ENV{SVN_LOG_EDITOR};
-if (!$editor) {
+if (!$editor || isCommitLogEditor($editor)) {
$editor = $ENV{CVS_LOG_EDITOR};
}
-if (!$editor) {
+if (!$editor || isCommitLogEditor($editor)) {
my $builtEditorApplication = "$baseDir/Release/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
$editor = $builtEditorApplication if -x $builtEditorApplication;
}
-if (!$editor) {
+if (!$editor || isCommitLogEditor($editor)) {
my $builtEditorApplication = "$baseDir/Debug/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
$editor = $builtEditorApplication if -x $builtEditorApplication;
}
-if (!$editor) {
+if (!$editor || isCommitLogEditor($editor)) {
my $installedEditorApplication = "$ENV{HOME}/Applications/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
$editor = $installedEditorApplication if -x $installedEditorApplication;
}
-if (!$editor) {
- $editor = $ENV{EDITOR} || "/usr/bin/vi";
+if (!$editor || isCommitLogEditor($editor)) {
+ $editor = $ENV{EDITOR};
+}
+if (!$editor || isCommitLogEditor($editor)) {
+ $editor = "/usr/bin/vi";
}
my $inChangesToBeCommitted = !isGit();
my @changeLogs = ();
my $logContents = "";
my $existingLog = 0;
-open LOG, $log or die;
+open LOG, $log or die "Could not open the log file.";
while (<LOG>) {
if (isGit()) {
if (/^# Changes to be committed:$/) {
@@ -102,7 +106,7 @@ while (<LOG>) {
}
$existingLog = isGit() && !(/^#/ || /^\s*$/) unless $existingLog;
- push @changeLogs, makeFilePathRelative($1) if $inChangesToBeCommitted && (/^M....(.*ChangeLog)\r?\n?$/ || /^#\tmodified: (.*ChangeLog)/) && !/-ChangeLog/;
+ push @changeLogs, makeFilePathRelative($1) if $inChangesToBeCommitted && (/^(?:M|A)....(.*ChangeLog)\r?\n?$/ || /^#\t(?:modified|new file): (.*ChangeLog)$/) && !/-ChangeLog$/;
}
close LOG;
@@ -151,8 +155,8 @@ for my $changeLog (@changeLogs) {
# Remove indentation spaces
$line =~ s/^ {8}//;
- # Save the reviewed by line
- if ($line =~ m/^Reviewed by .*/) {
+ # Save the reviewed / rubber stamped by line.
+ if ($line =~ m/^Reviewed by .*/ || $line =~ m/^Rubber[ \-]?stamped by .*/) {
$reviewedByLine = $line;
next;
}
@@ -184,7 +188,6 @@ for my $changeLog (@changeLogs) {
$reviewedByLine = "";
}
-
$lineCount++;
$contents .= $line;
} else {
@@ -204,12 +207,6 @@ for my $changeLog (@changeLogs) {
my $sortKey = lc $label;
if ($label eq "top level") {
$sortKey = "";
- } elsif ($label eq "Tools") {
- $sortKey = "-, just after top level";
- } elsif ($label eq "WebBrowser") {
- $sortKey = lc "WebKit, WebBrowser after";
- } elsif ($label eq "WebCore") {
- $sortKey = lc "WebFoundation, WebCore after";
} elsif ($label eq "LayoutTests") {
$sortKey = lc "~, LayoutTests last";
}
@@ -307,3 +304,9 @@ sub removeLongestCommonPrefixEndingInDoubleNewline(\%)
}
return substr($prefix, 0, $lastDoubleNewline + 2);
}
+
+sub isCommitLogEditor($)
+{
+ my $editor = shift;
+ return $editor =~ m/commit-log-editor/;
+}
diff --git a/WebKitTools/Scripts/debug-minibrowser b/WebKitTools/Scripts/debug-minibrowser
new file mode 100755
index 0000000..06685b4
--- /dev/null
+++ b/WebKitTools/Scripts/debug-minibrowser
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2005, 2007 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.
+# 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.
+
+# Simplified "debug" script for debugging the WebKit2 MiniBrowser.
+
+use strict;
+use FindBin;
+use lib $FindBin::Bin;
+use webkitdirs;
+
+setConfiguration();
+
+exit exitStatus(debugMiniBrowser());
diff --git a/WebKitTools/Scripts/new-run-webkit-httpd b/WebKitTools/Scripts/new-run-webkit-httpd
new file mode 100755
index 0000000..88ae84e
--- /dev/null
+++ b/WebKitTools/Scripts/new-run-webkit-httpd
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A utility script for starting and stopping the HTTP server with the
+ same configuration as used in the layout tests."""
+
+#
+# FIXME: currently this code only works with the Chromium ports and LigHTTPd.
+# It should be made to work on all ports.
+#
+# This script is also used by Chromium's ui_tests to run http layout tests
+# in a browser.
+#
+import optparse
+import os
+import sys
+import tempfile
+
+scripts_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
+webkitpy_directory = os.path.join(scripts_directory, "webkitpy")
+sys.path.append(os.path.join(webkitpy_directory, "layout_tests"))
+
+import port
+from port import http_server
+
+def run(options):
+ if not options.server:
+ print ('Usage: %s --server {start|stop} [--root=root_dir]'
+ ' [--port=port_number]' % sys.argv[0])
+ else:
+ if (options.root is None) and (options.port is not None):
+ # specifying root but not port means we want httpd on default
+ # set of ports that LayoutTest use, but pointing to a different
+ # source of tests. Specifying port but no root does not seem
+ # meaningful.
+ raise 'Specifying port requires also a root.'
+ port_obj = port.get(None, options)
+ httpd = http_server.Lighttpd(port_obj,
+ tempfile.gettempdir(),
+ port=options.port,
+ root=options.root,
+ register_cygwin=options.register_cygwin,
+ run_background=options.run_background)
+ if options.server == 'start':
+ httpd.start()
+ else:
+ httpd.stop(force=True)
+
+
+def main():
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('-k', '--server',
+ help='Server action (start|stop)')
+ option_parser.add_option('-p', '--port',
+ help='Port to listen on (overrides layout test ports)')
+ option_parser.add_option('-r', '--root',
+ help='Absolute path to DocumentRoot (overrides layout test roots)')
+ option_parser.add_option('--register_cygwin', action="store_true",
+ dest="register_cygwin", help='Register Cygwin paths (on Win try bots)')
+ option_parser.add_option('--run_background', action="store_true",
+ dest="run_background",
+ help='Run on background (for running as UI test)')
+ options, args = option_parser.parse_args()
+
+ # FIXME: Make this work with other ports as well.
+ options.chromium = True
+
+ run(options)
+
+
+if '__main__' == __name__:
+ main()
diff --git a/WebKitTools/Scripts/webkitpy/steps/commit.py b/WebKitTools/Scripts/new-run-webkit-tests
index dd1fed7..2ebe1da 100644..100755
--- a/WebKitTools/Scripts/webkitpy/steps/commit.py
+++ b/WebKitTools/Scripts/new-run-webkit-tests
@@ -1,9 +1,10 @@
+#!/usr/bin/env python
# Copyright (C) 2010 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
@@ -13,7 +14,7 @@
# * 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
@@ -26,10 +27,11 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
+"""Wrapper around webkitpy/layout_tests/run_webkit_tests.py"""
+import sys
+import webkitpy.layout_tests.run_webkit_tests as run_webkit_tests
-class Commit(AbstractStep):
- def run(self, state):
- commit_message = self._tool.scm().commit_message_for_this_commit()
- state["commit_text"] = self._tool.scm().commit_with_message(commit_message.message())
+if __name__ == '__main__':
+ options, args = run_webkit_tests.parse_args()
+ sys.exit(run_webkit_tests.main(options, args))
diff --git a/WebKitTools/Scripts/new-run-webkit-websocketserver b/WebKitTools/Scripts/new-run-webkit-websocketserver
new file mode 100644
index 0000000..8e4aeaa
--- /dev/null
+++ b/WebKitTools/Scripts/new-run-webkit-websocketserver
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A utility script for starting and stopping the web socket server with the
+ same configuration as used in the layout tests."""
+
+import logging
+import optparse
+import tempfile
+
+import webkitpy.layout_tests.port.factory as factory
+import webkitpy.layout_tests.port.websocket_server as websocket_server
+
+
+def main():
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('--server', type='choice',
+ choices=['start', 'stop'], default='start',
+ help='Server action (start|stop)')
+ option_parser.add_option('-p', '--port', dest='port',
+ default=None, help='Port to listen on')
+ option_parser.add_option('-r', '--root',
+ help='Absolute path to DocumentRoot '
+ '(overrides layout test roots)')
+ option_parser.add_option('-t', '--tls', dest='use_tls',
+ action='store_true',
+ default=False, help='use TLS (wss://)')
+ option_parser.add_option('-k', '--private_key', dest='private_key',
+ default='', help='TLS private key file.')
+ option_parser.add_option('-c', '--certificate', dest='certificate',
+ default='', help='TLS certificate file.')
+ option_parser.add_option('--register_cygwin', action="store_true",
+ dest="register_cygwin",
+ help='Register Cygwin paths (on Win try bots)')
+ option_parser.add_option('--pidfile', help='path to pid file.')
+ option_parser.add_option('-v', '--verbose', action='store_true',
+ default=False, help='include debug-level logging')
+ options, args = option_parser.parse_args()
+
+ if not options.port:
+ if options.use_tls:
+ # FIXME: We shouldn't grab at this private variable.
+ options.port = websocket_server._DEFAULT_WSS_PORT
+ else:
+ # FIXME: We shouldn't grab at this private variable.
+ options.port = websocket_server._DEFAULT_WS_PORT
+
+ kwds = {'port': options.port, 'use_tls': options.use_tls}
+ if options.root:
+ kwds['root'] = options.root
+ if options.private_key:
+ kwds['private_key'] = options.private_key
+ if options.certificate:
+ kwds['certificate'] = options.certificate
+ kwds['register_cygwin'] = options.register_cygwin
+ if options.pidfile:
+ kwds['pidfile'] = options.pidfile
+
+ port_obj = factory.get()
+ pywebsocket = websocket_server.PyWebSocket(port_obj, tempfile.gettempdir(), **kwds)
+
+ log_level = logging.WARN
+ if options.verbose:
+ log_level = logging.DEBUG
+ logging.basicConfig(level=log_level)
+
+ if 'start' == options.server:
+ pywebsocket.start()
+ else:
+ pywebsocket.stop(force=True)
+
+if '__main__' == __name__:
+ main()
diff --git a/WebKitTools/Scripts/old-run-webkit-tests b/WebKitTools/Scripts/old-run-webkit-tests
new file mode 100755
index 0000000..d5d7349
--- /dev/null
+++ b/WebKitTools/Scripts/old-run-webkit-tests
@@ -0,0 +1,2281 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+# Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com)
+# Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2009 Andras Becsi (becsi.andras@stud.u-szeged.hu), University of Szeged
+#
+# 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.
+
+# Script to run the WebKit Open Source Project layout tests.
+
+# Run all the tests passed in on the command line.
+# If no tests are passed, find all the .html, .shtml, .xml, .xhtml, .xhtmlmp, .pl, .php (and svg) files in the test directory.
+
+# Run each text.
+# Compare against the existing file xxx-expected.txt.
+# If there is a mismatch, generate xxx-actual.txt and xxx-diffs.txt.
+
+# At the end, report:
+# the number of tests that got the expected results
+# the number of tests that ran, but did not get the expected results
+# the number of tests that failed to run
+# the number of tests that were run but had no expected results to compare against
+
+use strict;
+use warnings;
+
+use Cwd;
+use Data::Dumper;
+use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
+use File::Basename;
+use File::Copy;
+use File::Find;
+use File::Path;
+use File::Spec;
+use File::Spec::Functions;
+use File::Temp;
+use FindBin;
+use Getopt::Long;
+use IPC::Open2;
+use IPC::Open3;
+use Time::HiRes qw(time usleep);
+
+use List::Util 'shuffle';
+
+use lib $FindBin::Bin;
+use webkitperl::features;
+use webkitperl::httpd;
+use webkitdirs;
+use VCSUtils;
+use POSIX;
+
+sub buildPlatformResultHierarchy();
+sub buildPlatformTestHierarchy(@);
+sub closeCygpaths();
+sub closeDumpTool();
+sub closeWebSocketServer();
+sub configureAndOpenHTTPDIfNeeded();
+sub countAndPrintLeaks($$$);
+sub countFinishedTest($$$$);
+sub deleteExpectedAndActualResults($);
+sub dumpToolDidCrash();
+sub epiloguesAndPrologues($$);
+sub expectedDirectoryForTest($;$;$);
+sub fileNameWithNumber($$);
+sub htmlForResultsSection(\@$&);
+sub isTextOnlyTest($);
+sub launchWithEnv(\@\%);
+sub resolveAndMakeTestResultsDirectory();
+sub numericcmp($$);
+sub openDiffTool();
+sub openDumpTool();
+sub parseLeaksandPrintUniqueLeaks();
+sub openWebSocketServerIfNeeded();
+sub pathcmp($$);
+sub printFailureMessageForTest($$);
+sub processIgnoreTests($$);
+sub readFromDumpToolWithTimer(**);
+sub readSkippedFiles($);
+sub recordActualResultsAndDiff($$);
+sub sampleDumpTool();
+sub setFileHandleNonBlocking(*$);
+sub slowestcmp($$);
+sub splitpath($);
+sub stopRunningTestsEarlyIfNeeded();
+sub stripExtension($);
+sub stripMetrics($$);
+sub testCrashedOrTimedOut($$$$$);
+sub toURL($);
+sub toWindowsPath($);
+sub validateSkippedArg($$;$);
+sub writeToFile($$);
+
+# Argument handling
+my $addPlatformExceptions = 0;
+my $complexText = 0;
+my $exitAfterNFailures = 0;
+my $generateNewResults = isAppleMacWebKit() ? 1 : 0;
+my $guardMalloc = '';
+# FIXME: Dynamic HTTP-port configuration in this file is wrong. The various
+# apache config files in LayoutTests/http/config govern the port numbers.
+# Dynamic configuration as-written will also cause random failures in
+# an IPv6 environment. See https://bugs.webkit.org/show_bug.cgi?id=37104.
+my $httpdPort = 8000;
+my $httpdSSLPort = 8443;
+my $ignoreMetrics = 0;
+my $webSocketPort = 8880;
+# wss is disabled until all platforms support pyOpenSSL.
+# my $webSocketSecurePort = 9323;
+my $ignoreTests = '';
+my $iterations = 1;
+my $launchSafari = 1;
+my $mergeDepth;
+my $pixelTests = '';
+my $platform;
+my $quiet = '';
+my $randomizeTests = 0;
+my $repeatEach = 1;
+my $report10Slowest = 0;
+my $resetResults = 0;
+my $reverseTests = 0;
+my $root;
+my $runSample = 1;
+my $shouldCheckLeaks = 0;
+my $showHelp = 0;
+my $stripEditingCallbacks = isCygwin();
+my $testHTTP = 1;
+my $testMedia = 1;
+my $tmpDir = "/tmp";
+my $testResultsDirectory = File::Spec->catfile($tmpDir, "layout-test-results");
+my $testsPerDumpTool = 1000;
+my $threaded = 0;
+# DumpRenderTree has an internal timeout of 30 seconds, so this must be > 30.
+my $timeoutSeconds = 35;
+my $tolerance = 0;
+my $treatSkipped = "default";
+my $useRemoteLinksToTests = 0;
+my $useValgrind = 0;
+my $verbose = 0;
+my $shouldWaitForHTTPD = 0;
+
+my @leaksFilenames;
+
+if (isWindows() || isMsys()) {
+ print "This script has to be run under Cygwin to function correctly.\n";
+ exit 1;
+}
+
+# Default to --no-http for wx for now.
+$testHTTP = 0 if (isWx());
+
+my $expectedTag = "expected";
+my $actualTag = "actual";
+my $prettyDiffTag = "pretty-diff";
+my $diffsTag = "diffs";
+my $errorTag = "stderr";
+
+my @macPlatforms = ("mac-tiger", "mac-leopard", "mac-snowleopard", "mac");
+
+if (isAppleMacWebKit()) {
+ if (isTiger()) {
+ $platform = "mac-tiger";
+ $tolerance = 1.0;
+ } elsif (isLeopard()) {
+ $platform = "mac-leopard";
+ $tolerance = 0.1;
+ } elsif (isSnowLeopard()) {
+ $platform = "mac-snowleopard";
+ $tolerance = 0.1;
+ } else {
+ $platform = "mac";
+ }
+} elsif (isQt()) {
+ if (isDarwin()) {
+ $platform = "qt-mac";
+ } elsif (isLinux()) {
+ $platform = "qt-linux";
+ } elsif (isWindows() || isCygwin()) {
+ $platform = "qt-win";
+ } else {
+ $platform = "qt";
+ }
+} elsif (isGtk()) {
+ $platform = "gtk";
+ if (!$ENV{"WEBKIT_TESTFONTS"}) {
+ print "The WEBKIT_TESTFONTS environment variable is not defined.\n";
+ print "You must set it before running the tests.\n";
+ print "Use git to grab the actual fonts from http://gitorious.org/qtwebkit/testfonts\n";
+ exit 1;
+ }
+} elsif (isWx()) {
+ $platform = "wx";
+} elsif (isCygwin()) {
+ $platform = "win";
+}
+
+if (!defined($platform)) {
+ print "WARNING: Your platform is not recognized. Any platform-specific results will be generated in platform/undefined.\n";
+ $platform = "undefined";
+}
+
+my $programName = basename($0);
+my $launchSafariDefault = $launchSafari ? "launch" : "do not launch";
+my $httpDefault = $testHTTP ? "run" : "do not run";
+my $sampleDefault = $runSample ? "run" : "do not run";
+
+my $usage = <<EOF;
+Usage: $programName [options] [testdir|testpath ...]
+ --add-platform-exceptions Put new results for non-platform-specific failing tests into the platform-specific results directory
+ --complex-text Use the complex text code path for all text (Mac OS X and Windows only)
+ -c|--configuration config Set DumpRenderTree build configuration
+ -g|--guard-malloc Enable malloc guard
+ --exit-after-n-failures N Exit after the first N failures instead of running all tests
+ -h|--help Show this help message
+ --[no-]http Run (or do not run) http tests (default: $httpDefault)
+ --[no-]wait-for-httpd Wait for httpd if some other test session is using it already (same as WEBKIT_WAIT_FOR_HTTPD=1). (default: $shouldWaitForHTTPD)
+ -i|--ignore-tests Comma-separated list of directories or tests to ignore
+ --iterations n Number of times to run the set of tests (e.g. ABCABCABC)
+ --[no-]launch-safari Launch (or do not launch) Safari to display test results (default: $launchSafariDefault)
+ -l|--leaks Enable leaks checking
+ --[no-]new-test-results Generate results for new tests
+ --nthly n Restart DumpRenderTree every n tests (default: $testsPerDumpTool)
+ -p|--pixel-tests Enable pixel tests
+ --tolerance t Ignore image differences less than this percentage (default: $tolerance)
+ --platform Override the detected platform to use for tests and results (default: $platform)
+ --port Web server port to use with http tests
+ -q|--quiet Less verbose output
+ --reset-results Reset ALL results (including pixel tests if --pixel-tests is set)
+ -o|--results-directory Output results directory (default: $testResultsDirectory)
+ --random Run the tests in a random order
+ --repeat-each n Number of times to run each test (e.g. AAABBBCCC)
+ --reverse Run the tests in reverse alphabetical order
+ --root Path to root tools build
+ --[no-]sample-on-timeout Run sample on timeout (default: $sampleDefault) (Mac OS X only)
+ -1|--singly Isolate each test case run (implies --nthly 1 --verbose)
+ --skipped=[default|ignore|only] Specifies how to treat the Skipped file
+ default: Tests/directories listed in the Skipped file are not tested
+ ignore: The Skipped file is ignored
+ only: Only those tests/directories listed in the Skipped file will be run
+ --slowest Report the 10 slowest tests
+ --ignore-metrics Ignore metrics in tests
+ --[no-]strip-editing-callbacks Remove editing callbacks from expected results
+ -t|--threaded Run a concurrent JavaScript thead with each test
+ --timeout t Sets the number of seconds before a test times out (default: $timeoutSeconds)
+ --valgrind Run DumpRenderTree inside valgrind (Qt/Linux only)
+ -v|--verbose More verbose output (overrides --quiet)
+ -m|--merge-leak-depth arg Merges leak callStacks and prints the number of unique leaks beneath a callstack depth of arg. Defaults to 5.
+ --use-remote-links-to-tests Link to test files within the SVN repository in the results.
+EOF
+
+setConfiguration();
+
+my $getOptionsResult = GetOptions(
+ 'add-platform-exceptions' => \$addPlatformExceptions,
+ 'complex-text' => \$complexText,
+ 'exit-after-n-failures=i' => \$exitAfterNFailures,
+ 'guard-malloc|g' => \$guardMalloc,
+ 'help|h' => \$showHelp,
+ 'http!' => \$testHTTP,
+ 'wait-for-httpd!' => \$shouldWaitForHTTPD,
+ 'ignore-metrics!' => \$ignoreMetrics,
+ 'ignore-tests|i=s' => \$ignoreTests,
+ 'iterations=i' => \$iterations,
+ 'launch-safari!' => \$launchSafari,
+ 'leaks|l' => \$shouldCheckLeaks,
+ 'merge-leak-depth|m:5' => \$mergeDepth,
+ 'new-test-results!' => \$generateNewResults,
+ 'nthly=i' => \$testsPerDumpTool,
+ 'pixel-tests|p' => \$pixelTests,
+ 'platform=s' => \$platform,
+ 'port=i' => \$httpdPort,
+ 'quiet|q' => \$quiet,
+ 'random' => \$randomizeTests,
+ 'repeat-each=i' => \$repeatEach,
+ 'reset-results' => \$resetResults,
+ 'results-directory|o=s' => \$testResultsDirectory,
+ 'reverse' => \$reverseTests,
+ 'root=s' => \$root,
+ 'sample-on-timeout!' => \$runSample,
+ 'singly|1' => sub { $testsPerDumpTool = 1; },
+ 'skipped=s' => \&validateSkippedArg,
+ 'slowest' => \$report10Slowest,
+ 'strip-editing-callbacks!' => \$stripEditingCallbacks,
+ 'threaded|t' => \$threaded,
+ 'timeout=i' => \$timeoutSeconds,
+ 'tolerance=f' => \$tolerance,
+ 'use-remote-links-to-tests' => \$useRemoteLinksToTests,
+ 'valgrind' => \$useValgrind,
+ 'verbose|v' => \$verbose,
+);
+
+if (!$getOptionsResult || $showHelp) {
+ print STDERR $usage;
+ exit 1;
+}
+
+my $ignoreSkipped = $treatSkipped eq "ignore";
+my $skippedOnly = $treatSkipped eq "only";
+
+my $configuration = configuration();
+
+# We need an environment variable to be able to enable the feature per-slave
+$shouldWaitForHTTPD = $ENV{"WEBKIT_WAIT_FOR_HTTPD"} unless ($shouldWaitForHTTPD);
+$verbose = 1 if $testsPerDumpTool == 1;
+
+if ($shouldCheckLeaks && $testsPerDumpTool > 1000) {
+ print STDERR "\nWARNING: Running more than 1000 tests at a time with MallocStackLogging enabled may cause a crash.\n\n";
+}
+
+# Stack logging does not play well with QuickTime on Tiger (rdar://problem/5537157)
+$testMedia = 0 if $shouldCheckLeaks && isTiger();
+
+# Generating remote links causes a lot of unnecessary spew on GTK build bot
+$useRemoteLinksToTests = 0 if isGtk();
+
+setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root));
+my $productDir = productDir();
+$productDir .= "/bin" if isQt();
+$productDir .= "/Programs" if isGtk();
+
+chdirWebKit();
+
+if (!defined($root)) {
+ print STDERR "Running build-dumprendertree\n";
+
+ local *DEVNULL;
+ my ($childIn, $childOut, $childErr);
+ if ($quiet) {
+ open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
+ $childOut = ">&DEVNULL";
+ $childErr = ">&DEVNULL";
+ } else {
+ # When not quiet, let the child use our stdout/stderr.
+ $childOut = ">&STDOUT";
+ $childErr = ">&STDERR";
+ }
+
+ my @args = argumentsForConfiguration();
+ my $buildProcess = open3($childIn, $childOut, $childErr, "WebKitTools/Scripts/build-dumprendertree", @args) or die "Failed to run build-dumprendertree";
+ close($childIn);
+ waitpid $buildProcess, 0;
+ my $buildResult = $?;
+ close($childOut);
+ close($childErr);
+
+ close DEVNULL if ($quiet);
+
+ if ($buildResult) {
+ print STDERR "Compiling DumpRenderTree failed!\n";
+ exit exitStatus($buildResult);
+ }
+}
+
+my $dumpToolName = "DumpRenderTree";
+$dumpToolName .= "_debug" if isCygwin() && configurationForVisualStudio() !~ /^Release|Debug_Internal$/;
+my $dumpTool = "$productDir/$dumpToolName";
+die "can't find executable $dumpToolName (looked in $productDir)\n" unless -x $dumpTool;
+
+my $imageDiffTool = "$productDir/ImageDiff";
+$imageDiffTool .= "_debug" if isCygwin() && configurationForVisualStudio() !~ /^Release|Debug_Internal$/;
+die "can't find executable $imageDiffTool (looked in $productDir)\n" if $pixelTests && !-x $imageDiffTool;
+
+checkFrameworks() unless isCygwin();
+
+if (isAppleMacWebKit()) {
+ push @INC, $productDir;
+ require DumpRenderTreeSupport;
+}
+
+my $layoutTestsName = "LayoutTests";
+my $testDirectory = File::Spec->rel2abs($layoutTestsName);
+my $expectedDirectory = $testDirectory;
+my $platformBaseDirectory = catdir($testDirectory, "platform");
+my $platformTestDirectory = catdir($platformBaseDirectory, $platform);
+my @platformResultHierarchy = buildPlatformResultHierarchy();
+my @platformTestHierarchy = buildPlatformTestHierarchy(@platformResultHierarchy);
+
+$expectedDirectory = $ENV{"WebKitExpectedTestResultsDirectory"} if $ENV{"WebKitExpectedTestResultsDirectory"};
+
+$testResultsDirectory = File::Spec->rel2abs($testResultsDirectory);
+my $testResults = File::Spec->catfile($testResultsDirectory, "results.html");
+
+if (isAppleMacWebKit()) {
+ print STDERR "Compiling Java tests\n";
+ my $javaTestsDirectory = catdir($testDirectory, "java");
+
+ if (system("/usr/bin/make", "-C", "$javaTestsDirectory")) {
+ exit 1;
+ }
+}
+
+
+print "Running tests from $testDirectory\n";
+if ($pixelTests) {
+ print "Enabling pixel tests with a tolerance of $tolerance%\n";
+ if (isDarwin()) {
+ print "WARNING: Temporarily changing the main display color profile:\n";
+ print "\tThe colors on your screen will change for the duration of the testing.\n";
+ print "\tThis allows the pixel tests to have consistent color values across all machines.\n";
+
+ if (isPerianInstalled()) {
+ print "WARNING: Perian's QuickTime component is installed and this may affect pixel test results!\n";
+ print "\tYou should avoid generating new pixel results in this environment.\n";
+ print "\tSee https://bugs.webkit.org/show_bug.cgi?id=22615 for details.\n";
+ }
+ }
+}
+
+system "ln", "-s", $testDirectory, "/tmp/LayoutTests" unless -x "/tmp/LayoutTests";
+
+my %ignoredFiles = ( "results.html" => 1 );
+my %ignoredDirectories = map { $_ => 1 } qw(platform);
+my %ignoredLocalDirectories = map { $_ => 1 } qw(.svn _svn resources script-tests);
+my %supportedFileExtensions = map { $_ => 1 } qw(html shtml xml xhtml xhtmlmp pl php);
+
+if (!checkWebCoreFeatureSupport("MathML", 0)) {
+ $ignoredDirectories{'mathml'} = 1;
+}
+
+# FIXME: We should fix webkitperl/features.pm:hasFeature() to do the correct feature detection for Cygwin.
+if (checkWebCoreFeatureSupport("SVG", 0)) {
+ $supportedFileExtensions{'svg'} = 1;
+} elsif (isCygwin()) {
+ $supportedFileExtensions{'svg'} = 1;
+} else {
+ $ignoredLocalDirectories{'svg'} = 1;
+}
+
+if (!$testHTTP) {
+ $ignoredDirectories{'http'} = 1;
+ $ignoredDirectories{'websocket'} = 1;
+}
+
+if (!$testMedia) {
+ $ignoredDirectories{'media'} = 1;
+ $ignoredDirectories{'http/tests/media'} = 1;
+}
+
+my $supportedFeaturesResult = "";
+
+if (isCygwin()) {
+ # Collect supported features list
+ setPathForRunningWebKitApp(\%ENV);
+ my $supportedFeaturesCommand = $dumpTool . " --print-supported-features 2>&1";
+ $supportedFeaturesResult = `$supportedFeaturesCommand 2>&1`;
+}
+
+my $hasAcceleratedCompositing = 0;
+my $has3DRendering = 0;
+
+if (isCygwin()) {
+ $hasAcceleratedCompositing = $supportedFeaturesResult =~ /AcceleratedCompositing/;
+ $has3DRendering = $supportedFeaturesResult =~ /3DRendering/;
+} else {
+ $hasAcceleratedCompositing = checkWebCoreFeatureSupport("Accelerated Compositing", 0);
+ $has3DRendering = checkWebCoreFeatureSupport("3D Rendering", 0);
+}
+
+if (!$hasAcceleratedCompositing) {
+ $ignoredDirectories{'compositing'} = 1;
+}
+
+if (!$has3DRendering) {
+ $ignoredDirectories{'animations/3d'} = 1;
+ $ignoredDirectories{'transforms/3d'} = 1;
+}
+
+if (!checkWebCoreFeatureSupport("3D Canvas", 0)) {
+ $ignoredDirectories{'fast/canvas/webgl'} = 1;
+}
+
+if (checkWebCoreFeatureSupport("WML", 0)) {
+ $supportedFileExtensions{'wml'} = 1;
+} else {
+ $ignoredDirectories{'http/tests/wml'} = 1;
+ $ignoredDirectories{'fast/wml'} = 1;
+ $ignoredDirectories{'wml'} = 1;
+}
+
+if (!checkWebCoreFeatureSupport("XHTMLMP", 0)) {
+ $ignoredDirectories{'fast/xhtmlmp'} = 1;
+}
+
+processIgnoreTests($ignoreTests, "ignore-tests") if $ignoreTests;
+if (!$ignoreSkipped) {
+ if (!$skippedOnly || @ARGV == 0) {
+ readSkippedFiles("");
+ } else {
+ # Since readSkippedFiles() appends to @ARGV, we must use a foreach
+ # loop so that we only iterate over the original argument list.
+ foreach my $argnum (0 .. $#ARGV) {
+ readSkippedFiles(shift @ARGV);
+ }
+ }
+}
+
+my @tests = findTestsToRun();
+
+die "no tests to run\n" if !@tests;
+
+my %counts;
+my %tests;
+my %imagesPresent;
+my %imageDifferences;
+my %durations;
+my $count = 0;
+my $leaksOutputFileNumber = 1;
+my $totalLeaks = 0;
+
+my @toolArgs = ();
+push @toolArgs, "--pixel-tests" if $pixelTests;
+push @toolArgs, "--threaded" if $threaded;
+push @toolArgs, "--complex-text" if $complexText;
+push @toolArgs, "-";
+
+my @diffToolArgs = ();
+push @diffToolArgs, "--tolerance", $tolerance;
+
+$| = 1;
+
+my $dumpToolPID;
+my $isDumpToolOpen = 0;
+my $dumpToolCrashed = 0;
+my $imageDiffToolPID;
+my $isDiffToolOpen = 0;
+
+my $atLineStart = 1;
+my $lastDirectory = "";
+
+my $isHttpdOpen = 0;
+my $isWebSocketServerOpen = 0;
+my $webSocketServerPID = 0;
+my $failedToStartWebSocketServer = 0;
+# wss is disabled until all platforms support pyOpenSSL.
+# my $webSocketSecureServerPID = 0;
+
+sub catch_pipe { $dumpToolCrashed = 1; }
+$SIG{"PIPE"} = "catch_pipe";
+
+print "Testing ", scalar @tests, " test cases";
+print " $iterations times" if ($iterations > 1);
+print ", repeating each test $repeatEach times" if ($repeatEach > 1);
+print ".\n";
+
+my $overallStartTime = time;
+
+my %expectedResultPaths;
+
+my @originalTests = @tests;
+# Add individual test repetitions
+if ($repeatEach > 1) {
+ @tests = ();
+ foreach my $test (@originalTests) {
+ for (my $i = 0; $i < $repeatEach; $i++) {
+ push(@tests, $test);
+ }
+ }
+}
+# Add test set repetitions
+for (my $i = 1; $i < $iterations; $i++) {
+ push(@tests, @originalTests);
+}
+
+for my $test (@tests) {
+ my $newDumpTool = not $isDumpToolOpen;
+ openDumpTool();
+
+ my $base = stripExtension($test);
+ my $expectedExtension = ".txt";
+
+ my $dir = $base;
+ $dir =~ s|/[^/]+$||;
+
+ if ($newDumpTool || $dir ne $lastDirectory) {
+ foreach my $logue (epiloguesAndPrologues($newDumpTool ? "" : $lastDirectory, $dir)) {
+ if (isCygwin()) {
+ $logue = toWindowsPath($logue);
+ } else {
+ $logue = canonpath($logue);
+ }
+ if ($verbose) {
+ print "running epilogue or prologue $logue\n";
+ }
+ print OUT "$logue\n";
+ # Throw away output from DumpRenderTree.
+ # Once for the test output and once for pixel results (empty)
+ while (<IN>) {
+ last if /#EOF/;
+ }
+ while (<IN>) {
+ last if /#EOF/;
+ }
+ }
+ }
+
+ if ($verbose) {
+ print "running $test -> ";
+ $atLineStart = 0;
+ } elsif (!$quiet) {
+ if ($dir ne $lastDirectory) {
+ print "\n" unless $atLineStart;
+ print "$dir ";
+ }
+ print ".";
+ $atLineStart = 0;
+ }
+
+ $lastDirectory = $dir;
+
+ my $result;
+
+ my $startTime = time if $report10Slowest;
+
+ # Try to read expected hash file for pixel tests
+ my $suffixExpectedHash = "";
+ if ($pixelTests && !$resetResults) {
+ my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
+ if (open EXPECTEDHASH, "$expectedPixelDir/$base-$expectedTag.checksum") {
+ my $expectedHash = <EXPECTEDHASH>;
+ chomp($expectedHash);
+ close EXPECTEDHASH;
+
+ # Format expected hash into a suffix string that is appended to the path / URL passed to DRT
+ $suffixExpectedHash = "'$expectedHash";
+ }
+ }
+
+ if ($test =~ /^http\//) {
+ configureAndOpenHTTPDIfNeeded();
+ if ($test !~ /^http\/tests\/local\// && $test !~ /^http\/tests\/ssl\// && $test !~ /^http\/tests\/wml\// && $test !~ /^http\/tests\/media\//) {
+ my $path = canonpath($test);
+ $path =~ s/^http\/tests\///;
+ print OUT "http://127.0.0.1:$httpdPort/$path$suffixExpectedHash\n";
+ } elsif ($test =~ /^http\/tests\/ssl\//) {
+ my $path = canonpath($test);
+ $path =~ s/^http\/tests\///;
+ print OUT "https://127.0.0.1:$httpdSSLPort/$path$suffixExpectedHash\n";
+ } else {
+ my $testPath = "$testDirectory/$test";
+ if (isCygwin()) {
+ $testPath = toWindowsPath($testPath);
+ } else {
+ $testPath = canonpath($testPath);
+ }
+ print OUT "$testPath$suffixExpectedHash\n";
+ }
+ } elsif ($test =~ /^websocket\//) {
+ if ($test =~ /^websocket\/tests\/local\//) {
+ my $testPath = "$testDirectory/$test";
+ if (isCygwin()) {
+ $testPath = toWindowsPath($testPath);
+ } else {
+ $testPath = canonpath($testPath);
+ }
+ print OUT "$testPath\n";
+ } else {
+ if (openWebSocketServerIfNeeded()) {
+ my $path = canonpath($test);
+ if ($test =~ /^websocket\/tests\/ssl\//) {
+ # wss is disabled until all platforms support pyOpenSSL.
+ print STDERR "Error: wss is disabled until all platforms support pyOpenSSL.";
+ # print OUT "https://127.0.0.1:$webSocketSecurePort/$path\n";
+ } else {
+ print OUT "http://127.0.0.1:$webSocketPort/$path\n";
+ }
+ } else {
+ # We failed to launch the WebSocket server. Display a useful error message rather than attempting
+ # to run tests that expect the server to be available.
+ my $errorMessagePath = "$testDirectory/websocket/resources/server-failed-to-start.html";
+ $errorMessagePath = isCygwin() ? toWindowsPath($errorMessagePath) : canonpath($errorMessagePath);
+ print OUT "$errorMessagePath\n";
+ }
+ }
+ } else {
+ my $testPath = "$testDirectory/$test";
+ if (isCygwin()) {
+ $testPath = toWindowsPath($testPath);
+ } else {
+ $testPath = canonpath($testPath);
+ }
+ print OUT "$testPath$suffixExpectedHash\n" if defined $testPath;
+ }
+
+ # DumpRenderTree is expected to dump two "blocks" to stdout for each test.
+ # Each block is terminated by a #EOF on a line by itself.
+ # The first block is the output of the test (in text, RenderTree or other formats).
+ # The second block is for optional pixel data in PNG format, and may be empty if
+ # pixel tests are not being run, or the test does not dump pixels (e.g. text tests).
+ my $readResults = readFromDumpToolWithTimer(IN, ERROR);
+
+ my $actual = $readResults->{output};
+ my $error = $readResults->{error};
+
+ $expectedExtension = $readResults->{extension};
+ my $expectedFileName = "$base-$expectedTag.$expectedExtension";
+
+ my $isText = isTextOnlyTest($actual);
+
+ my $expectedDir = expectedDirectoryForTest($base, $isText, $expectedExtension);
+ $expectedResultPaths{$base} = "$expectedDir/$expectedFileName";
+
+ unless ($readResults->{status} eq "success") {
+ my $crashed = $readResults->{status} eq "crashed";
+ testCrashedOrTimedOut($test, $base, $crashed, $actual, $error);
+ countFinishedTest($test, $base, $crashed ? "crash" : "timedout", 0);
+ last if stopRunningTestsEarlyIfNeeded();
+ next;
+ }
+
+ $durations{$test} = time - $startTime if $report10Slowest;
+
+ my $expected;
+
+ if (!$resetResults && open EXPECTED, "<", "$expectedDir/$expectedFileName") {
+ $expected = "";
+ while (<EXPECTED>) {
+ next if $stripEditingCallbacks && $_ =~ /^EDITING DELEGATE:/;
+ $expected .= $_;
+ }
+ close EXPECTED;
+ }
+
+ if ($ignoreMetrics && !$isText && defined $expected) {
+ ($actual, $expected) = stripMetrics($actual, $expected);
+ }
+
+ if ($shouldCheckLeaks && $testsPerDumpTool == 1) {
+ print " $test -> ";
+ }
+
+ my $actualPNG = "";
+ my $diffPNG = "";
+ my $diffPercentage = 0;
+ my $diffResult = "passed";
+
+ my $actualHash = "";
+ my $expectedHash = "";
+ my $actualPNGSize = 0;
+
+ while (<IN>) {
+ last if /#EOF/;
+ if (/ActualHash: ([a-f0-9]{32})/) {
+ $actualHash = $1;
+ } elsif (/ExpectedHash: ([a-f0-9]{32})/) {
+ $expectedHash = $1;
+ } elsif (/Content-Length: (\d+)\s*/) {
+ $actualPNGSize = $1;
+ read(IN, $actualPNG, $actualPNGSize);
+ }
+ }
+
+ if ($verbose && $pixelTests && !$resetResults && $actualPNGSize) {
+ if ($actualHash eq "" && $expectedHash eq "") {
+ printFailureMessageForTest($test, "WARNING: actual & expected pixel hashes are missing!");
+ } elsif ($actualHash eq "") {
+ printFailureMessageForTest($test, "WARNING: actual pixel hash is missing!");
+ } elsif ($expectedHash eq "") {
+ printFailureMessageForTest($test, "WARNING: expected pixel hash is missing!");
+ }
+ }
+
+ if ($actualPNGSize > 0) {
+ my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
+
+ if (!$resetResults && ($expectedHash ne $actualHash || ($actualHash eq "" && $expectedHash eq ""))) {
+ if (-f "$expectedPixelDir/$base-$expectedTag.png") {
+ my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png";
+ my $expectedPNG = "";
+ open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png";
+ read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
+
+ openDiffTool();
+ print DIFFOUT "Content-Length: $actualPNGSize\n";
+ print DIFFOUT $actualPNG;
+
+ print DIFFOUT "Content-Length: $expectedPNGSize\n";
+ print DIFFOUT $expectedPNG;
+
+ while (<DIFFIN>) {
+ last if /^error/ || /^diff:/;
+ if (/Content-Length: (\d+)\s*/) {
+ read(DIFFIN, $diffPNG, $1);
+ }
+ }
+
+ if (/^diff: (.+)% (passed|failed)/) {
+ $diffPercentage = $1 + 0;
+ $imageDifferences{$base} = $diffPercentage;
+ $diffResult = $2;
+ }
+
+ if (!$diffPercentage) {
+ printFailureMessageForTest($test, "pixel hash failed (but pixel test still passes)");
+ }
+ } elsif ($verbose) {
+ printFailureMessageForTest($test, "WARNING: expected image is missing!");
+ }
+ }
+
+ if ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png") {
+ mkpath catfile($expectedPixelDir, dirname($base)) if $testDirectory ne $expectedPixelDir;
+ writeToFile("$expectedPixelDir/$base-$expectedTag.png", $actualPNG);
+ }
+
+ if ($actualHash ne "" && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.checksum")) {
+ writeToFile("$expectedPixelDir/$base-$expectedTag.checksum", $actualHash);
+ }
+ }
+
+ if (dumpToolDidCrash()) {
+ $result = "crash";
+ testCrashedOrTimedOut($test, $base, 1, $actual, $error);
+ } elsif (!defined $expected) {
+ if ($verbose) {
+ print "new " . ($resetResults ? "result" : "test") ."\n";
+ $atLineStart = 1;
+ }
+ $result = "new";
+
+ if ($generateNewResults || $resetResults) {
+ mkpath catfile($expectedDir, dirname($base)) if $testDirectory ne $expectedDir;
+ writeToFile("$expectedDir/$expectedFileName", $actual);
+ }
+ deleteExpectedAndActualResults($base);
+ recordActualResultsAndDiff($base, $actual);
+ if (!$resetResults) {
+ # Always print the file name for new tests, as they will probably need some manual inspection.
+ # in verbose mode we already printed the test case, so no need to do it again.
+ unless ($verbose) {
+ print "\n" unless $atLineStart;
+ print "$test -> ";
+ }
+ my $resultsDir = catdir($expectedDir, dirname($base));
+ if ($generateNewResults) {
+ print "new (results generated in $resultsDir)\n";
+ } else {
+ print "new\n";
+ }
+ $atLineStart = 1;
+ }
+ } elsif ($actual eq $expected && $diffResult eq "passed") {
+ if ($verbose) {
+ print "succeeded\n";
+ $atLineStart = 1;
+ }
+ $result = "match";
+ deleteExpectedAndActualResults($base);
+ } else {
+ $result = "mismatch";
+
+ my $pixelTestFailed = $pixelTests && $diffPNG && $diffPNG ne "";
+ my $testFailed = $actual ne $expected;
+
+ my $message = !$testFailed ? "pixel test failed" : "failed";
+
+ if (($testFailed || $pixelTestFailed) && $addPlatformExceptions) {
+ my $testBase = catfile($testDirectory, $base);
+ my $expectedBase = catfile($expectedDir, $base);
+ my $testIsMaximallyPlatformSpecific = $testBase =~ m|^\Q$platformTestDirectory\E/|;
+ my $expectedResultIsMaximallyPlatformSpecific = $expectedBase =~ m|^\Q$platformTestDirectory\E/|;
+ if (!$testIsMaximallyPlatformSpecific && !$expectedResultIsMaximallyPlatformSpecific) {
+ mkpath catfile($platformTestDirectory, dirname($base));
+ if ($testFailed) {
+ my $expectedFile = catfile($platformTestDirectory, "$expectedFileName");
+ writeToFile("$expectedFile", $actual);
+ }
+ if ($pixelTestFailed) {
+ my $expectedFile = catfile($platformTestDirectory, "$base-$expectedTag.checksum");
+ writeToFile("$expectedFile", $actualHash);
+
+ $expectedFile = catfile($platformTestDirectory, "$base-$expectedTag.png");
+ writeToFile("$expectedFile", $actualPNG);
+ }
+ $message .= " (results generated in $platformTestDirectory)";
+ }
+ }
+
+ printFailureMessageForTest($test, $message);
+
+ my $dir = "$testResultsDirectory/$base";
+ $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
+ my $testName = $1;
+ mkpath $dir;
+
+ deleteExpectedAndActualResults($base);
+ recordActualResultsAndDiff($base, $actual);
+
+ if ($pixelTestFailed) {
+ $imagesPresent{$base} = 1;
+
+ writeToFile("$testResultsDirectory/$base-$actualTag.png", $actualPNG);
+ writeToFile("$testResultsDirectory/$base-$diffsTag.png", $diffPNG);
+
+ my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
+ copy("$expectedPixelDir/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png");
+
+ open DIFFHTML, ">$testResultsDirectory/$base-$diffsTag.html" or die;
+ print DIFFHTML "<html>\n";
+ print DIFFHTML "<head>\n";
+ print DIFFHTML "<title>$base Image Compare</title>\n";
+ print DIFFHTML "<script language=\"Javascript\" type=\"text/javascript\">\n";
+ print DIFFHTML "var currentImage = 0;\n";
+ print DIFFHTML "var imageNames = new Array(\"Actual\", \"Expected\");\n";
+ print DIFFHTML "var imagePaths = new Array(\"$testName-$actualTag.png\", \"$testName-$expectedTag.png\");\n";
+ if (-f "$testDirectory/$base-w3c.png") {
+ copy("$testDirectory/$base-w3c.png", "$testResultsDirectory/$base-w3c.png");
+ print DIFFHTML "imageNames.push(\"W3C\");\n";
+ print DIFFHTML "imagePaths.push(\"$testName-w3c.png\");\n";
+ }
+ print DIFFHTML "function animateImage() {\n";
+ print DIFFHTML " var image = document.getElementById(\"animatedImage\");\n";
+ print DIFFHTML " var imageText = document.getElementById(\"imageText\");\n";
+ print DIFFHTML " image.src = imagePaths[currentImage];\n";
+ print DIFFHTML " imageText.innerHTML = imageNames[currentImage] + \" Image\";\n";
+ print DIFFHTML " currentImage = (currentImage + 1) % imageNames.length;\n";
+ print DIFFHTML " setTimeout('animateImage()',2000);\n";
+ print DIFFHTML "}\n";
+ print DIFFHTML "</script>\n";
+ print DIFFHTML "</head>\n";
+ print DIFFHTML "<body onLoad=\"animateImage();\">\n";
+ print DIFFHTML "<table>\n";
+ if ($diffPercentage) {
+ print DIFFHTML "<tr>\n";
+ print DIFFHTML "<td>Difference between images: <a href=\"$testName-$diffsTag.png\">$diffPercentage%</a></td>\n";
+ print DIFFHTML "</tr>\n";
+ }
+ print DIFFHTML "<tr>\n";
+ print DIFFHTML "<td><a href=\"" . toURL("$testDirectory/$test") . "\">test file</a></td>\n";
+ print DIFFHTML "</tr>\n";
+ print DIFFHTML "<tr>\n";
+ print DIFFHTML "<td id=\"imageText\" style=\"text-weight: bold;\">Actual Image</td>\n";
+ print DIFFHTML "</tr>\n";
+ print DIFFHTML "<tr>\n";
+ print DIFFHTML "<td><img src=\"$testName-$actualTag.png\" id=\"animatedImage\"></td>\n";
+ print DIFFHTML "</tr>\n";
+ print DIFFHTML "</table>\n";
+ print DIFFHTML "</body>\n";
+ print DIFFHTML "</html>\n";
+ }
+ }
+
+ if ($error) {
+ my $dir = "$testResultsDirectory/$base";
+ $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
+ mkpath $dir;
+
+ writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error);
+
+ $counts{error}++;
+ push @{$tests{error}}, $test;
+ }
+
+ countFinishedTest($test, $base, $result, $isText);
+ last if stopRunningTestsEarlyIfNeeded();
+}
+
+my $totalTestingTime = time - $overallStartTime;
+my $waitTime = getWaitTime();
+if ($waitTime > 0.1) {
+ my $normalizedTestingTime = $totalTestingTime - $waitTime;
+ printf "\n%0.2fs HTTPD waiting time\n", $waitTime . "";
+ printf "%0.2fs normalized testing time", $normalizedTestingTime . "";
+}
+printf "\n%0.2fs total testing time\n", $totalTestingTime . "";
+
+!$isDumpToolOpen || die "Failed to close $dumpToolName.\n";
+
+$isHttpdOpen = !closeHTTPD();
+closeWebSocketServer();
+
+# Because multiple instances of this script are running concurrently we cannot
+# safely delete this symlink.
+# system "rm /tmp/LayoutTests";
+
+# FIXME: Do we really want to check the image-comparison tool for leaks every time?
+if ($isDiffToolOpen && $shouldCheckLeaks) {
+ $totalLeaks += countAndPrintLeaks("ImageDiff", $imageDiffToolPID, "$testResultsDirectory/ImageDiff-leaks.txt");
+}
+
+if ($totalLeaks) {
+ if ($mergeDepth) {
+ parseLeaksandPrintUniqueLeaks();
+ } else {
+ print "\nWARNING: $totalLeaks total leaks found!\n";
+ print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2);
+ }
+}
+
+close IN;
+close OUT;
+close ERROR;
+
+if ($report10Slowest) {
+ print "\n\nThe 10 slowest tests:\n\n";
+ my $count = 0;
+ for my $test (sort slowestcmp keys %durations) {
+ printf "%0.2f secs: %s\n", $durations{$test}, $test;
+ last if ++$count == 10;
+ }
+}
+
+print "\n";
+
+if ($skippedOnly && $counts{"match"}) {
+ print "The following tests are in the Skipped file (" . File::Spec->abs2rel("$platformTestDirectory/Skipped", $testDirectory) . "), but succeeded:\n";
+ foreach my $test (@{$tests{"match"}}) {
+ print " $test\n";
+ }
+}
+
+if ($resetResults || ($counts{match} && $counts{match} == $count)) {
+ print "all $count test cases succeeded\n";
+ unlink $testResults;
+ exit;
+}
+
+printResults();
+
+mkpath $testResultsDirectory;
+
+open HTML, ">", $testResults or die "Failed to open $testResults. $!";
+print HTML "<html>\n";
+print HTML "<head>\n";
+print HTML "<title>Layout Test Results</title>\n";
+print HTML "</head>\n";
+print HTML "<body>\n";
+
+if ($ignoreMetrics) {
+ print HTML "<h4>Tested with metrics ignored.</h4>";
+}
+
+print HTML htmlForResultsSection(@{$tests{mismatch}}, "Tests where results did not match expected results", \&linksForMismatchTest);
+print HTML htmlForResultsSection(@{$tests{timedout}}, "Tests that timed out", \&linksForErrorTest);
+print HTML htmlForResultsSection(@{$tests{crash}}, "Tests that caused the DumpRenderTree tool to crash", \&linksForErrorTest);
+print HTML htmlForResultsSection(@{$tests{error}}, "Tests that had stderr output", \&linksForErrorTest);
+print HTML htmlForResultsSection(@{$tests{new}}, "Tests that had no expected results (probably new)", \&linksForNewTest);
+
+print HTML "</body>\n";
+print HTML "</html>\n";
+close HTML;
+
+my @configurationArgs = argumentsForConfiguration();
+
+if (isGtk()) {
+ system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari;
+} elsif (isQt()) {
+ unshift @configurationArgs, qw(-graphicssystem raster -style windows);
+ if (isCygwin()) {
+ $testResults = "/" . toWindowsPath($testResults);
+ $testResults =~ s/\\/\//g;
+ }
+ system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari;
+} elsif (isCygwin()) {
+ system "cygstart", $testResults if $launchSafari;
+} else {
+ system "WebKitTools/Scripts/run-safari", @configurationArgs, "-NSOpen", $testResults if $launchSafari;
+}
+
+closeCygpaths() if isCygwin();
+
+exit 1;
+
+sub countAndPrintLeaks($$$)
+{
+ my ($dumpToolName, $dumpToolPID, $leaksFilePath) = @_;
+
+ print "\n" unless $atLineStart;
+ $atLineStart = 1;
+
+ # We are excluding the following reported leaks so they don't get in our way when looking for WebKit leaks:
+ # This allows us ignore known leaks and only be alerted when new leaks occur. Some leaks are in the old
+ # versions of the system frameworks that are being used by the leaks bots. Even though a leak has been
+ # fixed, it will be listed here until the bot has been updated with the newer frameworks.
+
+ my @typesToExclude = (
+ );
+
+ my @callStacksToExclude = (
+ "Flash_EnforceLocalSecurity" # leaks in Flash plug-in code, rdar://problem/4449747
+ );
+
+ if (isTiger()) {
+ # Leak list for the version of Tiger used on the build bot.
+ push @callStacksToExclude, (
+ "CFRunLoopRunSpecific \\| malloc_zone_malloc", "CFRunLoopRunSpecific \\| CFAllocatorAllocate ", # leak in CFRunLoopRunSpecific, rdar://problem/4670839
+ "CGImageSourceGetPropertiesAtIndex", # leak in ImageIO, rdar://problem/4628809
+ "FOGetCoveredUnicodeChars", # leak in ATS, rdar://problem/3943604
+ "GetLineDirectionPreference", "InitUnicodeUtilities", # leaks tool falsely reporting leak in CFNotificationCenterAddObserver, rdar://problem/4964790
+ "ICCFPrefWrapper::GetPrefDictionary", # leaks in Internet Config. code, rdar://problem/4449794
+ "NSHTTPURLProtocol setResponseHeader:", # leak in multipart/mixed-replace handling in Foundation, no Radar, but fixed in Leopard
+ "NSURLCache cachedResponseForRequest", # leak in CFURL cache, rdar://problem/4768430
+ "PCFragPrepareClosureFromFile", # leak in Code Fragment Manager, rdar://problem/3426998
+ "WebCore::Selection::toRange", # bug in 'leaks', rdar://problem/4967949
+ "WebCore::SubresourceLoader::create", # bug in 'leaks', rdar://problem/4985806
+ "_CFPreferencesDomainDeepCopyDictionary", # leak in CFPreferences, rdar://problem/4220786
+ "_objc_msgForward", # leak in NSSpellChecker, rdar://problem/4965278
+ "gldGetString", # leak in OpenGL, rdar://problem/5013699
+ "_setDefaultUserInfoFromURL", # leak in NSHTTPAuthenticator, rdar://problem/5546453
+ "SSLHandshake", # leak in SSL, rdar://problem/5546440
+ "SecCertificateCreateFromData", # leak in SSL code, rdar://problem/4464397
+ );
+ push @typesToExclude, (
+ "THRD", # bug in 'leaks', rdar://problem/3387783
+ "DRHT", # ditto (endian little hate i)
+ );
+ }
+
+ if (isLeopard()) {
+ # Leak list for the version of Leopard used on the build bot.
+ push @callStacksToExclude, (
+ "CFHTTPMessageAppendBytes", # leak in CFNetwork, rdar://problem/5435912
+ "sendDidReceiveDataCallback", # leak in CFNetwork, rdar://problem/5441619
+ "_CFHTTPReadStreamReadMark", # leak in CFNetwork, rdar://problem/5441468
+ "httpProtocolStart", # leak in CFNetwork, rdar://problem/5468837
+ "_CFURLConnectionSendCallbacks", # leak in CFNetwork, rdar://problem/5441600
+ "DispatchQTMsg", # leak in QuickTime, PPC only, rdar://problem/5667132
+ "QTMovieContentView createVisualContext", # leak in QuickTime, PPC only, rdar://problem/5667132
+ "_CopyArchitecturesForJVMVersion", # leak in Java, rdar://problem/5910823
+ );
+ }
+
+ if (isSnowLeopard()) {
+ push @callStacksToExclude, (
+ "readMakerNoteProps", # <rdar://problem/7156432> leak in ImageIO
+ "QTKitMovieControllerView completeUISetup", # <rdar://problem/7155156> leak in QTKit
+ "getVMInitArgs", # <rdar://problem/7714444> leak in Java
+ "Java_java_lang_System_initProperties", # <rdar://problem/7714465> leak in Java
+ );
+ }
+
+ if (isDarwin() && !isTiger() && !isLeopard() && !isSnowLeopard()) {
+ push @callStacksToExclude, (
+ "CGGradientCreateWithColorComponents", # leak in CoreGraphics, <rdar://problem/7888492>
+ );
+ }
+
+ my $leaksTool = sourceDir() . "/WebKitTools/Scripts/run-leaks";
+ my $excludeString = "--exclude-callstack '" . (join "' --exclude-callstack '", @callStacksToExclude) . "'";
+ $excludeString .= " --exclude-type '" . (join "' --exclude-type '", @typesToExclude) . "'" if @typesToExclude;
+
+ print " ? checking for leaks in $dumpToolName\n";
+ my $leaksOutput = `$leaksTool $excludeString $dumpToolPID`;
+ my ($count, $bytes) = $leaksOutput =~ /Process $dumpToolPID: (\d+) leaks? for (\d+) total/;
+ my ($excluded) = $leaksOutput =~ /(\d+) leaks? excluded/;
+
+ my $adjustedCount = $count;
+ $adjustedCount -= $excluded if $excluded;
+
+ if (!$adjustedCount) {
+ print " - no leaks found\n";
+ unlink $leaksFilePath;
+ return 0;
+ } else {
+ my $dir = $leaksFilePath;
+ $dir =~ s|/[^/]+$|| or die;
+ mkpath $dir;
+
+ if ($excluded) {
+ print " + $adjustedCount leaks ($bytes bytes including $excluded excluded leaks) were found, details in $leaksFilePath\n";
+ } else {
+ print " + $count leaks ($bytes bytes) were found, details in $leaksFilePath\n";
+ }
+
+ writeToFile($leaksFilePath, $leaksOutput);
+
+ push @leaksFilenames, $leaksFilePath;
+ }
+
+ return $adjustedCount;
+}
+
+sub writeToFile($$)
+{
+ my ($filePath, $contents) = @_;
+ open NEWFILE, ">", "$filePath" or die "Could not create $filePath. $!\n";
+ print NEWFILE $contents;
+ close NEWFILE;
+}
+
+# Break up a path into the directory (with slash) and base name.
+sub splitpath($)
+{
+ my ($path) = @_;
+
+ my $pathSeparator = "/";
+ my $dirname = dirname($path) . $pathSeparator;
+ $dirname = "" if $dirname eq "." . $pathSeparator;
+
+ return ($dirname, basename($path));
+}
+
+# Sort first by directory, then by file, so all paths in one directory are grouped
+# rather than being interspersed with items from subdirectories.
+# Use numericcmp to sort directory and filenames to make order logical.
+sub pathcmp($$)
+{
+ my ($patha, $pathb) = @_;
+
+ my ($dira, $namea) = splitpath($patha);
+ my ($dirb, $nameb) = splitpath($pathb);
+
+ return numericcmp($dira, $dirb) if $dira ne $dirb;
+ return numericcmp($namea, $nameb);
+}
+
+# Sort numeric parts of strings as numbers, other parts as strings.
+# Makes 1.33 come after 1.3, which is cool.
+sub numericcmp($$)
+{
+ my ($aa, $bb) = @_;
+
+ my @a = split /(\d+)/, $aa;
+ my @b = split /(\d+)/, $bb;
+
+ # Compare one chunk at a time.
+ # Each chunk is either all numeric digits, or all not numeric digits.
+ while (@a && @b) {
+ my $a = shift @a;
+ my $b = shift @b;
+
+ # Use numeric comparison if chunks are non-equal numbers.
+ return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b;
+
+ # Use string comparison if chunks are any other kind of non-equal string.
+ return $a cmp $b if $a ne $b;
+ }
+
+ # One of the two is now empty; compare lengths for result in this case.
+ return @a <=> @b;
+}
+
+# Sort slowest tests first.
+sub slowestcmp($$)
+{
+ my ($testa, $testb) = @_;
+
+ my $dura = $durations{$testa};
+ my $durb = $durations{$testb};
+ return $durb <=> $dura if $dura != $durb;
+ return pathcmp($testa, $testb);
+}
+
+sub launchWithEnv(\@\%)
+{
+ my ($args, $env) = @_;
+
+ # Dump the current environment as perl code and then put it in quotes so it is one parameter.
+ my $environmentDumper = Data::Dumper->new([\%{$env}], [qw(*ENV)]);
+ $environmentDumper->Indent(0);
+ $environmentDumper->Purity(1);
+ my $allEnvVars = $environmentDumper->Dump();
+ unshift @{$args}, "\"$allEnvVars\"";
+
+ my $execScript = File::Spec->catfile(sourceDir(), qw(WebKitTools Scripts execAppWithEnv));
+ unshift @{$args}, $execScript;
+ return @{$args};
+}
+
+sub resolveAndMakeTestResultsDirectory()
+{
+ my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory);
+ mkpath $absTestResultsDirectory;
+ return $absTestResultsDirectory;
+}
+
+sub openDiffTool()
+{
+ return if $isDiffToolOpen;
+ return if !$pixelTests;
+
+ my %CLEAN_ENV;
+ $CLEAN_ENV{MallocStackLogging} = 1 if $shouldCheckLeaks;
+ $imageDiffToolPID = open2(\*DIFFIN, \*DIFFOUT, $imageDiffTool, launchWithEnv(@diffToolArgs, %CLEAN_ENV)) or die "unable to open $imageDiffTool\n";
+ $isDiffToolOpen = 1;
+}
+
+sub openDumpTool()
+{
+ return if $isDumpToolOpen;
+
+ my %CLEAN_ENV;
+
+ # Generic environment variables
+ if (defined $ENV{'WEBKIT_TESTFONTS'}) {
+ $CLEAN_ENV{WEBKIT_TESTFONTS} = $ENV{'WEBKIT_TESTFONTS'};
+ }
+
+ # unique temporary directory for each DumpRendertree - needed for running more DumpRenderTree in parallel
+ $CLEAN_ENV{DUMPRENDERTREE_TEMP} = File::Temp::tempdir('DumpRenderTree-XXXXXX', TMPDIR => 1, CLEANUP => 1);
+ $CLEAN_ENV{XML_CATALOG_FILES} = ""; # work around missing /etc/catalog <rdar://problem/4292995>
+
+ # Platform spesifics
+ if (isLinux()) {
+ if (defined $ENV{'DISPLAY'}) {
+ $CLEAN_ENV{DISPLAY} = $ENV{'DISPLAY'};
+ } else {
+ $CLEAN_ENV{DISPLAY} = ":1";
+ }
+ if (defined $ENV{'XAUTHORITY'}) {
+ $CLEAN_ENV{XAUTHORITY} = $ENV{'XAUTHORITY'};
+ }
+
+ $CLEAN_ENV{HOME} = $ENV{'HOME'};
+
+ if (defined $ENV{'LD_LIBRARY_PATH'}) {
+ $CLEAN_ENV{LD_LIBRARY_PATH} = $ENV{'LD_LIBRARY_PATH'};
+ }
+ if (defined $ENV{'DBUS_SESSION_BUS_ADDRESS'}) {
+ $CLEAN_ENV{DBUS_SESSION_BUS_ADDRESS} = $ENV{'DBUS_SESSION_BUS_ADDRESS'};
+ }
+ } elsif (isDarwin()) {
+ if (defined $ENV{'DYLD_LIBRARY_PATH'}) {
+ $CLEAN_ENV{DYLD_LIBRARY_PATH} = $ENV{'DYLD_LIBRARY_PATH'};
+ }
+
+ $CLEAN_ENV{DYLD_FRAMEWORK_PATH} = $productDir;
+ $CLEAN_ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc;
+ } elsif (isCygwin()) {
+ $CLEAN_ENV{HOMEDRIVE} = $ENV{'HOMEDRIVE'};
+ $CLEAN_ENV{HOMEPATH} = $ENV{'HOMEPATH'};
+
+ setPathForRunningWebKitApp(\%CLEAN_ENV);
+ }
+
+ # Port spesifics
+ if (isQt()) {
+ $CLEAN_ENV{QTWEBKIT_PLUGIN_PATH} = productDir() . "/lib/plugins";
+ }
+
+ my @args = ($dumpTool, @toolArgs);
+ if (isAppleMacWebKit() and !isTiger()) {
+ unshift @args, "arch", "-" . architecture();
+ }
+
+ if ($useValgrind) {
+ unshift @args, "valgrind", "--suppressions=$platformBaseDirectory/qt/SuppressedValgrindErrors";
+ }
+
+ $CLEAN_ENV{MallocStackLogging} = 1 if $shouldCheckLeaks;
+
+ $dumpToolPID = open3(\*OUT, \*IN, \*ERROR, launchWithEnv(@args, %CLEAN_ENV)) or die "Failed to start tool: $dumpTool\n";
+ $isDumpToolOpen = 1;
+ $dumpToolCrashed = 0;
+}
+
+sub closeDumpTool()
+{
+ return if !$isDumpToolOpen;
+
+ close IN;
+ close OUT;
+ waitpid $dumpToolPID, 0;
+
+ # check for WebCore counter leaks.
+ if ($shouldCheckLeaks) {
+ while (<ERROR>) {
+ print;
+ }
+ }
+ close ERROR;
+ $isDumpToolOpen = 0;
+}
+
+sub dumpToolDidCrash()
+{
+ return 1 if $dumpToolCrashed;
+ return 0 unless $isDumpToolOpen;
+ my $pid = waitpid(-1, WNOHANG);
+ return 1 if ($pid == $dumpToolPID);
+
+ # On Mac OS X, crashing may be significantly delayed by crash reporter.
+ return 0 unless isAppleMacWebKit();
+
+ return DumpRenderTreeSupport::processIsCrashing($dumpToolPID);
+}
+
+sub configureAndOpenHTTPDIfNeeded()
+{
+ return if $isHttpdOpen;
+ my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory();
+ my $listen = "127.0.0.1:$httpdPort";
+ my @args = (
+ "-c", "CustomLog \"$absTestResultsDirectory/access_log.txt\" common",
+ "-c", "ErrorLog \"$absTestResultsDirectory/error_log.txt\"",
+ "-C", "Listen $listen"
+ );
+
+ my @defaultArgs = getDefaultConfigForTestDirectory($testDirectory);
+ @args = (@defaultArgs, @args);
+
+ waitForHTTPDLock() if $shouldWaitForHTTPD;
+ $isHttpdOpen = openHTTPD(@args);
+}
+
+sub openWebSocketServerIfNeeded()
+{
+ return 1 if $isWebSocketServerOpen;
+ return 0 if $failedToStartWebSocketServer;
+
+ my $webSocketServerPath = "/usr/bin/python";
+ my $webSocketPythonPath = "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket";
+ my $webSocketHandlerDir = "$testDirectory";
+ my $webSocketHandlerScanDir = "$testDirectory/websocket/tests";
+ my $webSocketHandlerMapFile = "$webSocketHandlerScanDir/handler_map.txt";
+ my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem";
+ my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory();
+ my $logFile = "$absTestResultsDirectory/pywebsocket_log.txt";
+
+ my @args = (
+ "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py",
+ "--server-host", "127.0.0.1",
+ "--port", "$webSocketPort",
+ "--document-root", "$webSocketHandlerDir",
+ "--scan-dir", "$webSocketHandlerScanDir",
+ "--websock-handlers-map-file", "$webSocketHandlerMapFile",
+ "--cgi-paths", "/websocket/tests",
+ "--log-file", "$logFile",
+ "--strict",
+ );
+ # wss is disabled until all platforms support pyOpenSSL.
+ # my @argsSecure = (
+ # "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py",
+ # "-p", "$webSocketSecurePort",
+ # "-d", "$webSocketHandlerDir",
+ # "-t",
+ # "-k", "$sslCertificate",
+ # "-c", "$sslCertificate",
+ # );
+
+ $ENV{"PYTHONPATH"} = $webSocketPythonPath;
+ $webSocketServerPID = open3(\*WEBSOCKETSERVER_IN, \*WEBSOCKETSERVER_OUT, \*WEBSOCKETSERVER_ERR, $webSocketServerPath, @args);
+ # wss is disabled until all platforms support pyOpenSSL.
+ # $webSocketSecureServerPID = open3(\*WEBSOCKETSECURESERVER_IN, \*WEBSOCKETSECURESERVER_OUT, \*WEBSOCKETSECURESERVER_ERR, $webSocketServerPath, @argsSecure);
+ # my @listen = ("http://127.0.0.1:$webSocketPort", "https://127.0.0.1:$webSocketSecurePort");
+ my @listen = ("http://127.0.0.1:$webSocketPort");
+ for (my $i = 0; $i < @listen; $i++) {
+ my $retryCount = 10;
+ while (system("/usr/bin/curl -k -q --silent --stderr - --output /dev/null $listen[$i]") && $retryCount) {
+ sleep 1;
+ --$retryCount;
+ }
+ unless ($retryCount) {
+ print STDERR "Timed out waiting for WebSocketServer to start.\n";
+ $failedToStartWebSocketServer = 1;
+ return 0;
+ }
+ }
+
+ $isWebSocketServerOpen = 1;
+ return 1;
+}
+
+sub closeWebSocketServer()
+{
+ return if !$isWebSocketServerOpen;
+
+ close WEBSOCKETSERVER_IN;
+ close WEBSOCKETSERVER_OUT;
+ close WEBSOCKETSERVER_ERR;
+ kill 15, $webSocketServerPID;
+
+ # wss is disabled until all platforms support pyOpenSSL.
+ # close WEBSOCKETSECURESERVER_IN;
+ # close WEBSOCKETSECURESERVER_OUT;
+ # close WEBSOCKETSECURESERVER_ERR;
+ # kill 15, $webSocketSecureServerPID;
+
+ $isWebSocketServerOpen = 0;
+}
+
+sub fileNameWithNumber($$)
+{
+ my ($base, $number) = @_;
+ return "$base$number" if ($number > 1);
+ return $base;
+}
+
+sub processIgnoreTests($$)
+{
+ my @ignoreList = split(/\s*,\s*/, shift);
+ my $listName = shift;
+
+ my $disabledSuffix = "-disabled";
+
+ my $addIgnoredDirectories = sub {
+ return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
+ $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)} = 1;
+ return @_;
+ };
+ foreach my $item (@ignoreList) {
+ my $path = catfile($testDirectory, $item);
+ if (-d $path) {
+ $ignoredDirectories{$item} = 1;
+ find({ preprocess => $addIgnoredDirectories, wanted => sub {} }, $path);
+ }
+ elsif (-f $path) {
+ $ignoredFiles{$item} = 1;
+ } elsif (-f $path . $disabledSuffix) {
+ # The test is disabled, so do nothing.
+ } else {
+ print "$listName list contained '$item', but no file of that name could be found\n";
+ }
+ }
+}
+
+sub stripExtension($)
+{
+ my ($test) = @_;
+
+ $test =~ s/\.[a-zA-Z]+$//;
+ return $test;
+}
+
+sub isTextOnlyTest($)
+{
+ my ($actual) = @_;
+ my $isText;
+ if ($actual =~ /^layer at/ms) {
+ $isText = 0;
+ } else {
+ $isText = 1;
+ }
+ return $isText;
+}
+
+sub expectedDirectoryForTest($;$;$)
+{
+ my ($base, $isText, $expectedExtension) = @_;
+
+ my @directories = @platformResultHierarchy;
+ push @directories, map { catdir($platformBaseDirectory, $_) } qw(mac-snowleopard mac) if isCygwin();
+ push @directories, $expectedDirectory;
+
+ # If we already have expected results, just return their location.
+ foreach my $directory (@directories) {
+ return $directory if (-f "$directory/$base-$expectedTag.$expectedExtension");
+ }
+
+ # For cross-platform tests, text-only results should go in the cross-platform directory,
+ # while render tree dumps should go in the least-specific platform directory.
+ return $isText ? $expectedDirectory : $platformResultHierarchy[$#platformResultHierarchy];
+}
+
+sub countFinishedTest($$$$)
+{
+ my ($test, $base, $result, $isText) = @_;
+
+ if (($count + 1) % $testsPerDumpTool == 0 || $count == $#tests) {
+ if ($shouldCheckLeaks) {
+ my $fileName;
+ if ($testsPerDumpTool == 1) {
+ $fileName = "$testResultsDirectory/$base-leaks.txt";
+ } else {
+ $fileName = "$testResultsDirectory/" . fileNameWithNumber($dumpToolName, $leaksOutputFileNumber) . "-leaks.txt";
+ }
+ my $leakCount = countAndPrintLeaks($dumpToolName, $dumpToolPID, $fileName);
+ $totalLeaks += $leakCount;
+ $leaksOutputFileNumber++ if ($leakCount);
+ }
+
+ closeDumpTool();
+ }
+
+ $count++;
+ $counts{$result}++;
+ push @{$tests{$result}}, $test;
+}
+
+sub testCrashedOrTimedOut($$$$$)
+{
+ my ($test, $base, $didCrash, $actual, $error) = @_;
+
+ printFailureMessageForTest($test, $didCrash ? "crashed" : "timed out");
+
+ sampleDumpTool() unless $didCrash;
+
+ my $dir = "$testResultsDirectory/$base";
+ $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
+ mkpath $dir;
+
+ deleteExpectedAndActualResults($base);
+
+ if (defined($error) && length($error)) {
+ writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error);
+ }
+
+ recordActualResultsAndDiff($base, $actual);
+
+ kill 9, $dumpToolPID unless $didCrash;
+
+ closeDumpTool();
+}
+
+sub printFailureMessageForTest($$)
+{
+ my ($test, $description) = @_;
+
+ unless ($verbose) {
+ print "\n" unless $atLineStart;
+ print "$test -> ";
+ }
+ print "$description\n";
+ $atLineStart = 1;
+}
+
+my %cygpaths = ();
+
+sub openCygpathIfNeeded($)
+{
+ my ($options) = @_;
+
+ return unless isCygwin();
+ return $cygpaths{$options} if $cygpaths{$options} && $cygpaths{$options}->{"open"};
+
+ local (*CYGPATHIN, *CYGPATHOUT);
+ my $pid = open2(\*CYGPATHIN, \*CYGPATHOUT, "cygpath -f - $options");
+ my $cygpath = {
+ "pid" => $pid,
+ "in" => *CYGPATHIN,
+ "out" => *CYGPATHOUT,
+ "open" => 1
+ };
+
+ $cygpaths{$options} = $cygpath;
+
+ return $cygpath;
+}
+
+sub closeCygpaths()
+{
+ return unless isCygwin();
+
+ foreach my $cygpath (values(%cygpaths)) {
+ close $cygpath->{"in"};
+ close $cygpath->{"out"};
+ waitpid($cygpath->{"pid"}, 0);
+ $cygpath->{"open"} = 0;
+
+ }
+}
+
+sub convertPathUsingCygpath($$)
+{
+ my ($path, $options) = @_;
+
+ # cygpath -f (at least in Cygwin 1.7) converts spaces into newlines. We remove spaces here and
+ # add them back in after conversion to work around this.
+ my $spaceSubstitute = "__NOTASPACE__";
+ $path =~ s/ /\Q$spaceSubstitute\E/g;
+
+ my $cygpath = openCygpathIfNeeded($options);
+ local *inFH = $cygpath->{"in"};
+ local *outFH = $cygpath->{"out"};
+ print outFH $path . "\n";
+ my $convertedPath = <inFH>;
+ chomp($convertedPath) if defined $convertedPath;
+
+ $convertedPath =~ s/\Q$spaceSubstitute\E/ /g;
+ return $convertedPath;
+}
+
+sub toWindowsPath($)
+{
+ my ($path) = @_;
+ return unless isCygwin();
+
+ return convertPathUsingCygpath($path, "-w");
+}
+
+sub toURL($)
+{
+ my ($path) = @_;
+
+ if ($useRemoteLinksToTests) {
+ my $relativePath = File::Spec->abs2rel($path, $testDirectory);
+
+ # If the file is below the test directory then convert it into a link to the file in SVN
+ if ($relativePath !~ /^\.\.\//) {
+ my $revision = svnRevisionForDirectory($testDirectory);
+ my $svnPath = pathRelativeToSVNRepositoryRootForPath($path);
+ return "http://trac.webkit.org/export/$revision/$svnPath";
+ }
+ }
+
+ return $path unless isCygwin();
+
+ return "file:///" . convertPathUsingCygpath($path, "-m");
+}
+
+sub validateSkippedArg($$;$)
+{
+ my ($option, $value, $value2) = @_;
+ my %validSkippedValues = map { $_ => 1 } qw(default ignore only);
+ $value = lc($value);
+ die "Invalid argument '" . $value . "' for option $option" unless $validSkippedValues{$value};
+ $treatSkipped = $value;
+}
+
+sub htmlForResultsSection(\@$&)
+{
+ my ($tests, $description, $linkGetter) = @_;
+
+ my @html = ();
+ return join("\n", @html) unless @{$tests};
+
+ push @html, "<p>$description:</p>";
+ push @html, "<table>";
+ foreach my $test (@{$tests}) {
+ push @html, "<tr>";
+ push @html, "<td><a href=\"" . toURL("$testDirectory/$test") . "\">$test</a></td>";
+ foreach my $link (@{&{$linkGetter}($test)}) {
+ push @html, "<td><a href=\"$link->{href}\">$link->{text}</a></td>";
+ }
+ push @html, "</tr>";
+ }
+ push @html, "</table>";
+
+ return join("\n", @html);
+}
+
+sub linksForExpectedAndActualResults($)
+{
+ my ($base) = @_;
+
+ my @links = ();
+
+ return \@links unless -s "$testResultsDirectory/$base-$diffsTag.txt";
+
+ my $expectedResultPath = $expectedResultPaths{$base};
+ my ($expectedResultFileName, $expectedResultsDirectory, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
+
+ push @links, { href => "$base-$expectedTag$expectedResultExtension", text => "expected" };
+ push @links, { href => "$base-$actualTag$expectedResultExtension", text => "actual" };
+ push @links, { href => "$base-$diffsTag.txt", text => "diff" };
+ push @links, { href => "$base-$prettyDiffTag.html", text => "pretty diff" };
+
+ return \@links;
+}
+
+sub linksForMismatchTest
+{
+ my ($test) = @_;
+
+ my @links = ();
+
+ my $base = stripExtension($test);
+
+ push @links, @{linksForExpectedAndActualResults($base)};
+ return \@links unless $pixelTests && $imagesPresent{$base};
+
+ push @links, { href => "$base-$expectedTag.png", text => "expected image" };
+ push @links, { href => "$base-$diffsTag.html", text => "image diffs" };
+ push @links, { href => "$base-$diffsTag.png", text => "$imageDifferences{$base}%" };
+
+ return \@links;
+}
+
+sub linksForErrorTest
+{
+ my ($test) = @_;
+
+ my @links = ();
+
+ my $base = stripExtension($test);
+
+ push @links, @{linksForExpectedAndActualResults($base)};
+ push @links, { href => "$base-$errorTag.txt", text => "stderr" };
+
+ return \@links;
+}
+
+sub linksForNewTest
+{
+ my ($test) = @_;
+
+ my @links = ();
+
+ my $base = stripExtension($test);
+
+ my $expectedResultPath = $expectedResultPaths{$base};
+ my ($expectedResultFileName, $expectedResultsDirectory, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
+
+ push @links, { href => "$base-$actualTag$expectedResultExtension", text => "result" };
+ if ($pixelTests && $imagesPresent{$base}) {
+ push @links, { href => "$base-$expectedTag.png", text => "image" };
+ }
+
+ return \@links;
+}
+
+sub deleteExpectedAndActualResults($)
+{
+ my ($base) = @_;
+
+ unlink "$testResultsDirectory/$base-$actualTag.txt";
+ unlink "$testResultsDirectory/$base-$diffsTag.txt";
+ unlink "$testResultsDirectory/$base-$errorTag.txt";
+}
+
+sub recordActualResultsAndDiff($$)
+{
+ my ($base, $actualResults) = @_;
+
+ return unless defined($actualResults) && length($actualResults);
+
+ my $expectedResultPath = $expectedResultPaths{$base};
+ my ($expectedResultFileNameMinusExtension, $expectedResultDirectoryPath, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
+ my $actualResultsPath = "$testResultsDirectory/$base-$actualTag$expectedResultExtension";
+ my $copiedExpectedResultsPath = "$testResultsDirectory/$base-$expectedTag$expectedResultExtension";
+
+ mkpath(dirname($actualResultsPath));
+ writeToFile("$actualResultsPath", $actualResults);
+
+ if (-f $expectedResultPath) {
+ copy("$expectedResultPath", "$copiedExpectedResultsPath");
+ } else {
+ open EMPTY, ">$copiedExpectedResultsPath";
+ close EMPTY;
+ }
+
+ my $diffOuputBasePath = "$testResultsDirectory/$base";
+ my $diffOutputPath = "$diffOuputBasePath-$diffsTag.txt";
+ system "diff -u \"$copiedExpectedResultsPath\" \"$actualResultsPath\" > \"$diffOutputPath\"";
+
+ my $prettyDiffOutputPath = "$diffOuputBasePath-$prettyDiffTag.html";
+ my $prettyPatchPath = "BugsSite/PrettyPatch/";
+ my $prettifyPath = "$prettyPatchPath/prettify.rb";
+ system "ruby -I \"$prettyPatchPath\" \"$prettifyPath\" \"$diffOutputPath\" > \"$prettyDiffOutputPath\"";
+}
+
+sub buildPlatformResultHierarchy()
+{
+ mkpath($platformTestDirectory) if ($platform eq "undefined" && !-d "$platformTestDirectory");
+
+ my @platforms;
+ if ($platform =~ /^mac-/) {
+ my $i;
+ for ($i = 0; $i < @macPlatforms; $i++) {
+ last if $macPlatforms[$i] eq $platform;
+ }
+ for (; $i < @macPlatforms; $i++) {
+ push @platforms, $macPlatforms[$i];
+ }
+ } elsif ($platform =~ /^qt-/) {
+ push @platforms, $platform;
+ push @platforms, "qt";
+ } else {
+ @platforms = $platform;
+ }
+
+ my @hierarchy;
+ for (my $i = 0; $i < @platforms; $i++) {
+ my $scoped = catdir($platformBaseDirectory, $platforms[$i]);
+ push(@hierarchy, $scoped) if (-d $scoped);
+ }
+
+ return @hierarchy;
+}
+
+sub buildPlatformTestHierarchy(@)
+{
+ my (@platformHierarchy) = @_;
+ return @platformHierarchy if (@platformHierarchy < 2);
+
+ return ($platformHierarchy[0], $platformHierarchy[$#platformHierarchy]);
+}
+
+sub epiloguesAndPrologues($$)
+{
+ my ($lastDirectory, $directory) = @_;
+ my @lastComponents = split('/', $lastDirectory);
+ my @components = split('/', $directory);
+
+ while (@lastComponents) {
+ if (!defined($components[0]) || $lastComponents[0] ne $components[0]) {
+ last;
+ }
+ shift @components;
+ shift @lastComponents;
+ }
+
+ my @result;
+ my $leaving = $lastDirectory;
+ foreach (@lastComponents) {
+ my $epilogue = $leaving . "/resources/run-webkit-tests-epilogue.html";
+ foreach (@platformResultHierarchy) {
+ push @result, catdir($_, $epilogue) if (stat(catdir($_, $epilogue)));
+ }
+ push @result, catdir($testDirectory, $epilogue) if (stat(catdir($testDirectory, $epilogue)));
+ $leaving =~ s|(^\|/)[^/]+$||;
+ }
+
+ my $entering = $leaving;
+ foreach (@components) {
+ $entering .= '/' . $_;
+ my $prologue = $entering . "/resources/run-webkit-tests-prologue.html";
+ push @result, catdir($testDirectory, $prologue) if (stat(catdir($testDirectory, $prologue)));
+ foreach (reverse @platformResultHierarchy) {
+ push @result, catdir($_, $prologue) if (stat(catdir($_, $prologue)));
+ }
+ }
+ return @result;
+}
+
+sub parseLeaksandPrintUniqueLeaks()
+{
+ return unless @leaksFilenames;
+
+ my $mergedFilenames = join " ", @leaksFilenames;
+ my $parseMallocHistoryTool = sourceDir() . "/WebKitTools/Scripts/parse-malloc-history";
+
+ open MERGED_LEAKS, "cat $mergedFilenames | $parseMallocHistoryTool --merge-depth $mergeDepth - |" ;
+ my @leakLines = <MERGED_LEAKS>;
+ close MERGED_LEAKS;
+
+ my $uniqueLeakCount = 0;
+ my $totalBytes;
+ foreach my $line (@leakLines) {
+ ++$uniqueLeakCount if ($line =~ /^(\d*)\scalls/);
+ $totalBytes = $1 if $line =~ /^total\:\s(.*)\s\(/;
+ }
+
+ print "\nWARNING: $totalLeaks total leaks found for a total of $totalBytes!\n";
+ print "WARNING: $uniqueLeakCount unique leaks found!\n";
+ print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2);
+
+}
+
+sub extensionForMimeType($)
+{
+ my ($mimeType) = @_;
+
+ if ($mimeType eq "application/x-webarchive") {
+ return "webarchive";
+ } elsif ($mimeType eq "application/pdf") {
+ return "pdf";
+ }
+ return "txt";
+}
+
+# Read up to the first #EOF (the content block of the test), or until detecting crashes or timeouts.
+sub readFromDumpToolWithTimer(**)
+{
+ my ($fhIn, $fhError) = @_;
+
+ setFileHandleNonBlocking($fhIn, 1);
+ setFileHandleNonBlocking($fhError, 1);
+
+ my $maximumSecondsWithoutOutput = $timeoutSeconds;
+ $maximumSecondsWithoutOutput *= 10 if $guardMalloc;
+ my $microsecondsToWaitBeforeReadingAgain = 1000;
+
+ my $timeOfLastSuccessfulRead = time;
+
+ my @output = ();
+ my @error = ();
+ my $status = "success";
+ my $mimeType = "text/plain";
+ # We don't have a very good way to know when the "headers" stop
+ # and the content starts, so we use this as a hack:
+ my $haveSeenContentType = 0;
+ my $haveSeenEofIn = 0;
+ my $haveSeenEofError = 0;
+
+ while (1) {
+ if (time - $timeOfLastSuccessfulRead > $maximumSecondsWithoutOutput) {
+ $status = dumpToolDidCrash() ? "crashed" : "timedOut";
+ last;
+ }
+
+ # Once we've seen the EOF, we must not read anymore.
+ my $lineIn = readline($fhIn) unless $haveSeenEofIn;
+ my $lineError = readline($fhError) unless $haveSeenEofError;
+ if (!defined($lineIn) && !defined($lineError)) {
+ last if ($haveSeenEofIn && $haveSeenEofError);
+
+ if ($! != EAGAIN) {
+ $status = "crashed";
+ last;
+ }
+
+ # No data ready
+ usleep($microsecondsToWaitBeforeReadingAgain);
+ next;
+ }
+
+ $timeOfLastSuccessfulRead = time;
+
+ if (defined($lineIn)) {
+ if (!$haveSeenContentType && $lineIn =~ /^Content-Type: (\S+)$/) {
+ $mimeType = $1;
+ $haveSeenContentType = 1;
+ } elsif ($lineIn =~ /#EOF/) {
+ $haveSeenEofIn = 1;
+ } else {
+ push @output, $lineIn;
+ }
+ }
+ if (defined($lineError)) {
+ if ($lineError =~ /#EOF/) {
+ $haveSeenEofError = 1;
+ } else {
+ push @error, $lineError;
+ }
+ }
+ }
+
+ setFileHandleNonBlocking($fhIn, 0);
+ setFileHandleNonBlocking($fhError, 0);
+ return {
+ output => join("", @output),
+ error => join("", @error),
+ status => $status,
+ mimeType => $mimeType,
+ extension => extensionForMimeType($mimeType)
+ };
+}
+
+sub setFileHandleNonBlocking(*$)
+{
+ my ($fh, $nonBlocking) = @_;
+
+ my $flags = fcntl($fh, F_GETFL, 0) or die "Couldn't get filehandle flags";
+
+ if ($nonBlocking) {
+ $flags |= O_NONBLOCK;
+ } else {
+ $flags &= ~O_NONBLOCK;
+ }
+
+ fcntl($fh, F_SETFL, $flags) or die "Couldn't set filehandle flags";
+
+ return 1;
+}
+
+sub sampleDumpTool()
+{
+ return unless isAppleMacWebKit();
+ return unless $runSample;
+
+ my $outputDirectory = "$ENV{HOME}/Library/Logs/DumpRenderTree";
+ -d $outputDirectory or mkdir $outputDirectory;
+
+ my $outputFile = "$outputDirectory/HangReport.txt";
+ system "/usr/bin/sample", $dumpToolPID, qw(10 10 -file), $outputFile;
+}
+
+sub stripMetrics($$)
+{
+ my ($actual, $expected) = @_;
+
+ foreach my $result ($actual, $expected) {
+ $result =~ s/at \(-?[0-9]+,-?[0-9]+\) *//g;
+ $result =~ s/size -?[0-9]+x-?[0-9]+ *//g;
+ $result =~ s/text run width -?[0-9]+: //g;
+ $result =~ s/text run width -?[0-9]+ [a-zA-Z ]+: //g;
+ $result =~ s/RenderButton {BUTTON} .*/RenderButton {BUTTON}/g;
+ $result =~ s/RenderImage {INPUT} .*/RenderImage {INPUT}/g;
+ $result =~ s/RenderBlock {INPUT} .*/RenderBlock {INPUT}/g;
+ $result =~ s/RenderTextControl {INPUT} .*/RenderTextControl {INPUT}/g;
+ $result =~ s/\([0-9]+px/px/g;
+ $result =~ s/ *" *\n +" */ /g;
+ $result =~ s/" +$/"/g;
+
+ $result =~ s/- /-/g;
+ $result =~ s/\n( *)"\s+/\n$1"/g;
+ $result =~ s/\s+"\n/"\n/g;
+ $result =~ s/scrollWidth [0-9]+/scrollWidth/g;
+ $result =~ s/scrollHeight [0-9]+/scrollHeight/g;
+ }
+
+ return ($actual, $expected);
+}
+
+sub fileShouldBeIgnored
+{
+ my ($filePath) = @_;
+ foreach my $ignoredDir (keys %ignoredDirectories) {
+ if ($filePath =~ m/^$ignoredDir/) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub readSkippedFiles($)
+{
+ my ($constraintPath) = @_;
+
+ foreach my $level (@platformTestHierarchy) {
+ if (open SKIPPED, "<", "$level/Skipped") {
+ if ($verbose) {
+ my ($dir, $name) = splitpath($level);
+ print "Skipped tests in $name:\n";
+ }
+
+ while (<SKIPPED>) {
+ my $skipped = $_;
+ chomp $skipped;
+ $skipped =~ s/^[ \n\r]+//;
+ $skipped =~ s/[ \n\r]+$//;
+ if ($skipped && $skipped !~ /^#/) {
+ if ($skippedOnly) {
+ if (!fileShouldBeIgnored($skipped)) {
+ if (!$constraintPath) {
+ # Always add $skipped since no constraint path was specified on the command line.
+ push(@ARGV, $skipped);
+ } elsif ($skipped =~ /^($constraintPath)/) {
+ # Add $skipped only if it matches the current path constraint, e.g.,
+ # "--skipped=only dir1" with "dir1/file1.html" on the skipped list.
+ push(@ARGV, $skipped);
+ } elsif ($constraintPath =~ /^($skipped)/) {
+ # Add current path constraint if it is more specific than the skip list entry,
+ # e.g., "--skipped=only dir1/dir2/dir3" with "dir1" on the skipped list.
+ push(@ARGV, $constraintPath);
+ }
+ } elsif ($verbose) {
+ print " $skipped\n";
+ }
+ } else {
+ if ($verbose) {
+ print " $skipped\n";
+ }
+ processIgnoreTests($skipped, "Skipped");
+ }
+ }
+ }
+ close SKIPPED;
+ }
+ }
+}
+
+my @testsFound;
+
+sub directoryFilter
+{
+ return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
+ return () if exists $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)};
+ return @_;
+}
+
+sub fileFilter
+{
+ my $filename = $_;
+ if ($filename =~ /\.([^.]+)$/) {
+ if (exists $supportedFileExtensions{$1}) {
+ my $path = File::Spec->abs2rel(catfile($File::Find::dir, $filename), $testDirectory);
+ push @testsFound, $path if !exists $ignoredFiles{$path};
+ }
+ }
+}
+
+sub findTestsToRun
+{
+ my @testsToRun = ();
+
+ for my $test (@ARGV) {
+ $test =~ s/^($layoutTestsName|$testDirectory)\///;
+ my $fullPath = catfile($testDirectory, $test);
+ if (file_name_is_absolute($test)) {
+ print "can't run test $test outside $testDirectory\n";
+ } elsif (-f $fullPath) {
+ my ($filename, $pathname, $fileExtension) = fileparse($test, qr{\.[^.]+$});
+ if (!exists $supportedFileExtensions{substr($fileExtension, 1)}) {
+ print "test $test does not have a supported extension\n";
+ } elsif ($testHTTP || $pathname !~ /^http\//) {
+ push @testsToRun, $test;
+ }
+ } elsif (-d $fullPath) {
+ @testsFound = ();
+ find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $fullPath);
+ for my $level (@platformTestHierarchy) {
+ my $platformPath = catfile($level, $test);
+ find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $platformPath) if (-d $platformPath);
+ }
+ push @testsToRun, sort pathcmp @testsFound;
+ @testsFound = ();
+ } else {
+ print "test $test not found\n";
+ }
+ }
+
+ if (!scalar @ARGV) {
+ @testsFound = ();
+ find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $testDirectory);
+ for my $level (@platformTestHierarchy) {
+ find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $level);
+ }
+ push @testsToRun, sort pathcmp @testsFound;
+ @testsFound = ();
+
+ # We need to minimize the time when Apache and WebSocketServer is locked by tests
+ # so run them last if no explicit order was specified in the argument list.
+ my @httpTests;
+ my @websocketTests;
+ my @otherTests;
+ foreach my $test (@testsToRun) {
+ if ($test =~ /^http\//) {
+ push(@httpTests, $test);
+ } elsif ($test =~ /^websocket\//) {
+ push(@websocketTests, $test);
+ } else {
+ push(@otherTests, $test);
+ }
+ }
+ @testsToRun = (@otherTests, @httpTests, @websocketTests);
+ }
+
+ # Reverse the tests
+ @testsToRun = reverse @testsToRun if $reverseTests;
+
+ # Shuffle the array
+ @testsToRun = shuffle(@testsToRun) if $randomizeTests;
+
+ return @testsToRun;
+}
+
+sub printResults
+{
+ my %text = (
+ match => "succeeded",
+ mismatch => "had incorrect layout",
+ new => "were new",
+ timedout => "timed out",
+ crash => "crashed",
+ error => "had stderr output"
+ );
+
+ for my $type ("match", "mismatch", "new", "timedout", "crash", "error") {
+ my $typeCount = $counts{$type};
+ next unless $typeCount;
+ my $typeText = $text{$type};
+ my $message;
+ if ($typeCount == 1) {
+ $typeText =~ s/were/was/;
+ $message = sprintf "1 test case (%d%%) %s\n", 1 * 100 / $count, $typeText;
+ } else {
+ $message = sprintf "%d test cases (%d%%) %s\n", $typeCount, $typeCount * 100 / $count, $typeText;
+ }
+ $message =~ s-\(0%\)-(<1%)-;
+ print $message;
+ }
+}
+
+sub stopRunningTestsEarlyIfNeeded()
+{
+ # --reset-results does not check pass vs. fail, so exitAfterNFailures makes no sense with --reset-results.
+ return 0 if !$exitAfterNFailures || $resetResults;
+
+ my $passCount = $counts{match} || 0; # $counts{match} will be undefined if we've not yet passed a test (e.g. the first test fails).
+ my $failureCount = $count - $passCount; # "Failure" here includes new tests, timeouts, crashes, etc.
+ return 0 if $failureCount < $exitAfterNFailures;
+
+ print "\nExiting early after $failureCount failures. $count tests run.";
+ closeDumpTool();
+ return 1;
+}
diff --git a/WebKitTools/Scripts/prepare-ChangeLog b/WebKitTools/Scripts/prepare-ChangeLog
index 3350aa3..b087f67 100755
--- a/WebKitTools/Scripts/prepare-ChangeLog
+++ b/WebKitTools/Scripts/prepare-ChangeLog
@@ -89,6 +89,7 @@ sub get_function_line_ranges($$);
sub get_function_line_ranges_for_c($$);
sub get_function_line_ranges_for_java($$);
sub get_function_line_ranges_for_javascript($$);
+sub get_selector_line_ranges_for_css($$);
sub method_decl_to_selector($);
sub processPaths(\@);
sub reviewerAndDescriptionForGitCommit($);
@@ -101,6 +102,7 @@ my $changeLogTimeZone = "PST8PDT";
my $bugNumber;
my $name;
my $emailAddress;
+my $mergeBase = 0;
my $gitCommit = 0;
my $gitIndex = "";
my $gitReviewer = "";
@@ -114,6 +116,7 @@ my $parseOptionsResult =
"bug:i" => \$bugNumber,
"name:s" => \$name,
"email:s" => \$emailAddress,
+ "merge-base:s" => \$mergeBase,
"git-commit:s" => \$gitCommit,
"git-index" => \$gitIndex,
"git-reviewer:s" => \$gitReviewer,
@@ -125,6 +128,7 @@ if (!$parseOptionsResult || $showHelp) {
print STDERR basename($0) . " [--bug] [-d|--diff] [-h|--help] [-o|--open] [--git-commit=<committish>] [--git-reviewer=<name>] [svndir1 [svndir2 ...]]\n";
print STDERR " --bug Fill in the ChangeLog bug information from the given bug.\n";
print STDERR " -d|--diff Spew diff to stdout when running\n";
+ print STDERR " --merge-base Populate the ChangeLogs with the diff to this branch\n";
print STDERR " --git-commit Populate the ChangeLogs from the specified git commit\n";
print STDERR " --git-index Populate the ChangeLogs from the git index only\n";
print STDERR " --git-reviewer When populating the ChangeLogs from a git commit claim that the spcified name reviewed the change.\n";
@@ -474,6 +478,8 @@ sub get_function_line_ranges($$)
return get_function_line_ranges_for_java ($file_handle, $file_name);
} elsif ($file_name =~ /\.js$/) {
return get_function_line_ranges_for_javascript ($file_handle, $file_name);
+ } elsif ($file_name =~ /\.css$/) {
+ return get_selector_line_ranges_for_css ($file_handle, $file_name);
}
return ();
}
@@ -1173,6 +1179,41 @@ sub get_function_line_ranges_for_javascript($$)
return @ranges;
}
+# Read a file and get all the line ranges of the things that look like CSS selectors. A selector is
+# anything before an opening brace on a line. A selector starts at the line containing the opening
+# brace and ends at the closing brace.
+# FIXME: Comments are parsed just like uncommented text.
+#
+# Result is a list of triples: [ start_line, end_line, selector ].
+
+sub get_selector_line_ranges_for_css($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @ranges;
+
+ my $currentSelector = "";
+ my $start = 0;
+
+ while (<$fileHandle>) {
+ if (/^[ \t]*(.*[^ \t])[ \t]*{/) {
+ $currentSelector = $1;
+ $start = $.;
+ }
+ if (index($_, "}") >= 0) {
+ unless ($start) {
+ warn "mismatched braces in $fileName\n";
+ next;
+ }
+ push(@ranges, [$start, $., $currentSelector]);
+ $currentSelector = "";
+ $start = 0;
+ next;
+ }
+ }
+
+ return @ranges;
+}
sub processPaths(\@)
{
@@ -1216,6 +1257,7 @@ sub diffFromToString()
return $gitCommit if $gitCommit =~ m/.+\.\..+/;
return "\"$gitCommit^\" \"$gitCommit\"" if $gitCommit;
return "--cached" if $gitIndex;
+ return $mergeBase if $mergeBase;
return "HEAD" if $isGit;
}
@@ -1230,7 +1272,7 @@ sub diffCommand(@)
$command = "$SVN diff --diff-cmd diff -x -N $pathsString";
} elsif ($isGit) {
$command = "$GIT diff --no-ext-diff -U0 " . diffFromToString();
- $command .= " -- $pathsString" unless $gitCommit;
+ $command .= " -- $pathsString" unless $gitCommit or $mergeBase;
}
return $command;
diff --git a/WebKitTools/Scripts/rebaseline-chromium-webkit-tests b/WebKitTools/Scripts/rebaseline-chromium-webkit-tests
index 302995c..8d14b86 100755
--- a/WebKitTools/Scripts/rebaseline-chromium-webkit-tests
+++ b/WebKitTools/Scripts/rebaseline-chromium-webkit-tests
@@ -31,9 +31,12 @@
import os
import sys
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
- "webkitpy", "layout_tests"))
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))))
+scripts_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
+webkitpy_directory = os.path.join(scripts_directory, "webkitpy")
+sys.path.append(os.path.join(webkitpy_directory, "layout_tests"))
+
+# For simplejson
+sys.path.append(os.path.join(webkitpy_directory, "thirdparty"))
import rebaseline_chromium_webkit_tests
diff --git a/WebKitTools/Scripts/resolve-ChangeLogs b/WebKitTools/Scripts/resolve-ChangeLogs
index 3238350..6635711 100755
--- a/WebKitTools/Scripts/resolve-ChangeLogs
+++ b/WebKitTools/Scripts/resolve-ChangeLogs
@@ -49,7 +49,6 @@ sub fixMergedChangeLogs($;@);
sub fixOneMergedChangeLog($);
sub hasGitUnmergedFiles();
sub isInGitFilterBranch();
-sub mergeChanges($$$);
sub parseFixMerged($$;$);
sub removeChangeLogArguments($);
sub resolveChangeLog($);
@@ -130,11 +129,11 @@ if (defined $fixMerged && length($fixMerged) > 0) {
fixMergedChangeLogs($commitRange, @changeLogFiles);
} elsif ($mergeDriver) {
my ($base, $theirs, $ours) = @ARGV;
- if (mergeChanges($ours, $base, $theirs)) {
+ if (mergeChangeLogs($ours, $base, $theirs)) {
unlink($ours);
copy($theirs, $ours) or die $!;
} else {
- exit 1;
+ exec qw(git merge-file -L THEIRS -L BASE -L OURS), $theirs, $base, $ours;
}
} elsif (@changeLogFiles) {
for my $file (@changeLogFiles) {
@@ -401,55 +400,6 @@ sub isInGitFilterBranch()
return exists $ENV{MAPPED_PREVIOUS_COMMIT} && $ENV{MAPPED_PREVIOUS_COMMIT};
}
-sub mergeChanges($$$)
-{
- my ($fileMine, $fileOlder, $fileNewer) = @_;
-
- my $traditionalReject = $fileMine =~ /\.rej$/ ? 1 : 0;
-
- local $/ = undef;
-
- my $patch;
- if ($traditionalReject) {
- open(DIFF, "<", $fileMine) or die $!;
- $patch = <DIFF>;
- close(DIFF);
- rename($fileMine, "$fileMine.save");
- rename($fileOlder, "$fileOlder.save");
- } else {
- open(DIFF, "-|", qw(diff -u -a --binary), $fileOlder, $fileMine) or die $!;
- $patch = <DIFF>;
- close(DIFF);
- }
-
- unlink("${fileNewer}.orig");
- unlink("${fileNewer}.rej");
-
- open(PATCH, "| patch --fuzz=3 --binary $fileNewer > " . File::Spec->devnull()) or die $!;
- print PATCH fixChangeLogPatch($patch);
- close(PATCH);
-
- my $result;
-
- # Refuse to merge the patch if it did not apply cleanly
- if (-e "${fileNewer}.rej") {
- unlink("${fileNewer}.rej");
- unlink($fileNewer);
- rename("${fileNewer}.orig", $fileNewer);
- $result = 0;
- } else {
- unlink("${fileNewer}.orig");
- $result = 1;
- }
-
- if ($traditionalReject) {
- rename("$fileMine.save", $fileMine);
- rename("$fileOlder.save", $fileOlder);
- }
-
- return $result;
-}
-
sub parseFixMerged($$;$)
{
my ($switchName, $key, $value) = @_;
@@ -491,7 +441,7 @@ sub resolveChangeLog($)
return unless $fileMine && $fileOlder && $fileNewer;
- if (mergeChanges($fileMine, $fileOlder, $fileNewer)) {
+ if (mergeChangeLogs($fileMine, $fileOlder, $fileNewer)) {
if ($file ne $fileNewer) {
unlink($file);
rename($fileNewer, $file) or die $!;
diff --git a/WebKitTools/Scripts/run-launcher b/WebKitTools/Scripts/run-launcher
index e12a64a..bc00aac 100755
--- a/WebKitTools/Scripts/run-launcher
+++ b/WebKitTools/Scripts/run-launcher
@@ -64,6 +64,10 @@ if (isQt()) {
$launcherPath = catdir($launcherPath, "Programs", "GtkLauncher");
}
+ if (isEfl()) {
+ $launcherPath = catdir($launcherPath, "Programs", "EWebLauncher");
+ }
+
if (isWx()) {
if (isDarwin()) {
$launcherPath = catdir($launcherPath, 'wxBrowser.app', 'Contents', 'MacOS', 'wxBrowser');
diff --git a/WebKitTools/Scripts/run-minibrowser b/WebKitTools/Scripts/run-minibrowser
new file mode 100755
index 0000000..c2fd412
--- /dev/null
+++ b/WebKitTools/Scripts/run-minibrowser
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2005, 2007 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.
+# 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.
+
+# Simplified "run" script for launching the WebKit2 MiniBrowser.
+
+use strict;
+use FindBin;
+use lib $FindBin::Bin;
+use webkitdirs;
+
+setConfiguration();
+
+exit exitStatus(runMiniBrowser());
diff --git a/WebKitTools/Scripts/run-webkit-httpd b/WebKitTools/Scripts/run-webkit-httpd
index 018f64c..9ea2551 100755
--- a/WebKitTools/Scripts/run-webkit-httpd
+++ b/WebKitTools/Scripts/run-webkit-httpd
@@ -42,6 +42,10 @@ use lib $FindBin::Bin;
use webkitperl::httpd;
use webkitdirs;
+# FIXME: Dynamic HTTP-port configuration in this file is wrong. The various
+# apache config files in LayoutTests/http/config govern the port numbers.
+# Dynamic configuration as-written will also cause random failures in
+# an IPv6 environment. See https://bugs.webkit.org/show_bug.cgi?id=37104.
# Argument handling
my $httpdPort = 8000;
my $allInterfaces = 0;
diff --git a/WebKitTools/Scripts/run-webkit-tests b/WebKitTools/Scripts/run-webkit-tests
index 809e078..f28f20a 100755
--- a/WebKitTools/Scripts/run-webkit-tests
+++ b/WebKitTools/Scripts/run-webkit-tests
@@ -1,2222 +1,81 @@
#!/usr/bin/perl
-
-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
-# Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
-# Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com)
-# Copyright (C) 2007 Eric Seidel <eric@webkit.org>
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2009 Andras Becsi (becsi.andras@stud.u-szeged.hu), University of Szeged
+# Copyright (C) 2010 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:
+# 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.
+# * 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 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.
-
-# Script to run the WebKit Open Source Project layout tests.
-
-# Run all the tests passed in on the command line.
-# If no tests are passed, find all the .html, .shtml, .xml, .xhtml, .pl, .php (and svg) files in the test directory.
-
-# Run each text.
-# Compare against the existing file xxx-expected.txt.
-# If there is a mismatch, generate xxx-actual.txt and xxx-diffs.txt.
-
-# At the end, report:
-# the number of tests that got the expected results
-# the number of tests that ran, but did not get the expected results
-# the number of tests that failed to run
-# the number of tests that were run but had no expected results to compare against
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This file is a temporary hack.
+# It will be removed as soon as all platforms are are ready to move to
+# new-run-webkit-tests and we can then update the buildbots to explicitly
+# call old-run-webkit-tests for any platforms which will never support
+# a Python run-webkit-tests.
+
+# This is intentionally written in Perl to guarantee support on
+# the same set of platforms as old-run-webkit-tests currently supports.
+# The buildbot master.cfg also currently passes run-webkit-tests to
+# perl directly instead of executing it in a shell.
use strict;
use warnings;
-use Cwd;
-use Data::Dumper;
-use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
-use File::Basename;
-use File::Copy;
-use File::Find;
-use File::Path;
-use File::Spec;
-use File::Spec::Functions;
use FindBin;
-use Getopt::Long;
-use IPC::Open2;
-use IPC::Open3;
-use Time::HiRes qw(time usleep);
-
-use List::Util 'shuffle';
-
use lib $FindBin::Bin;
-use webkitperl::features;
-use webkitperl::httpd;
use webkitdirs;
-use VCSUtils;
-use POSIX;
-
-sub buildPlatformResultHierarchy();
-sub buildPlatformTestHierarchy(@);
-sub closeCygpaths();
-sub closeDumpTool();
-sub closeWebSocketServer();
-sub configureAndOpenHTTPDIfNeeded();
-sub countAndPrintLeaks($$$);
-sub countFinishedTest($$$$);
-sub deleteExpectedAndActualResults($);
-sub dumpToolDidCrash();
-sub epiloguesAndPrologues($$);
-sub expectedDirectoryForTest($;$;$);
-sub fileNameWithNumber($$);
-sub htmlForResultsSection(\@$&);
-sub isTextOnlyTest($);
-sub launchWithEnv(\@\%);
-sub resolveAndMakeTestResultsDirectory();
-sub numericcmp($$);
-sub openDiffTool();
-sub openDumpTool();
-sub parseLeaksandPrintUniqueLeaks();
-sub openWebSocketServerIfNeeded();
-sub pathcmp($$);
-sub printFailureMessageForTest($$);
-sub processIgnoreTests($$);
-sub readFromDumpToolWithTimer(**);
-sub readSkippedFiles($);
-sub recordActualResultsAndDiff($$);
-sub sampleDumpTool();
-sub setFileHandleNonBlocking(*$);
-sub slowestcmp($$);
-sub splitpath($);
-sub stripExtension($);
-sub stripMetrics($$);
-sub testCrashedOrTimedOut($$$$$);
-sub toURL($);
-sub toWindowsPath($);
-sub validateSkippedArg($$;$);
-sub writeToFile($$);
-
-# Argument handling
-my $addPlatformExceptions = 0;
-my $complexText = 0;
-my $exitAfterNFailures = 0;
-my $generateNewResults = isAppleMacWebKit() ? 1 : 0;
-my $guardMalloc = '';
-my $httpdPort = 8000;
-my $httpdSSLPort = 8443;
-my $ignoreMetrics = 0;
-my $webSocketPort = 8880;
-# wss is disabled until all platforms support pyOpenSSL.
-# my $webSocketSecurePort = 9323;
-my $ignoreTests = '';
-my $iterations = 1;
-my $launchSafari = 1;
-my $mergeDepth;
-my $pixelTests = '';
-my $platform;
-my $quiet = '';
-my $randomizeTests = 0;
-my $repeatEach = 1;
-my $report10Slowest = 0;
-my $resetResults = 0;
-my $reverseTests = 0;
-my $root;
-my $runSample = 1;
-my $shouldCheckLeaks = 0;
-my $showHelp = 0;
-my $stripEditingCallbacks = isCygwin();
-my $testHTTP = 1;
-my $testMedia = 1;
-my $tmpDir = "/tmp";
-my $testResultsDirectory = File::Spec->catfile($tmpDir, "layout-test-results");
-my $testsPerDumpTool = 1000;
-my $threaded = 0;
-# DumpRenderTree has an internal timeout of 15 seconds, so this must be > 15.
-my $timeoutSeconds = 20;
-my $tolerance = 0;
-my $treatSkipped = "default";
-my $useRemoteLinksToTests = 0;
-my $useValgrind = 0;
-my $verbose = 0;
-my $shouldWaitForHTTPD = 0;
-
-my @leaksFilenames;
-
-if (isWindows() || isMsys()) {
- print "This script has to be run under Cygwin to function correctly.\n";
- exit 1;
-}
-
-# Default to --no-http for wx for now.
-$testHTTP = 0 if (isWx());
-
-my $expectedTag = "expected";
-my $actualTag = "actual";
-my $prettyDiffTag = "pretty-diff";
-my $diffsTag = "diffs";
-my $errorTag = "stderr";
-
-my @macPlatforms = ("mac-tiger", "mac-leopard", "mac-snowleopard", "mac");
-
-if (isAppleMacWebKit()) {
- if (isTiger()) {
- $platform = "mac-tiger";
- $tolerance = 1.0;
- } elsif (isLeopard()) {
- $platform = "mac-leopard";
- $tolerance = 0.1;
- } elsif (isSnowLeopard()) {
- $platform = "mac-snowleopard";
- $tolerance = 0.1;
- } else {
- $platform = "mac";
- }
-} elsif (isQt()) {
- if (isDarwin()) {
- $platform = "qt-mac";
- } elsif (isLinux()) {
- $platform = "qt-linux";
- } elsif (isWindows() || isCygwin()) {
- $platform = "qt-win";
- } else {
- $platform = "qt";
- }
-} elsif (isGtk()) {
- $platform = "gtk";
- if (!$ENV{"WEBKIT_TESTFONTS"}) {
- print "The WEBKIT_TESTFONTS environment variable is not defined.\n";
- print "You must set it before running the tests.\n";
- print "Use git to grab the actual fonts from http://gitorious.org/qtwebkit/testfonts\n";
- exit 1;
- }
-} elsif (isWx()) {
- $platform = "wx";
-} elsif (isCygwin()) {
- $platform = "win";
-}
-
-if (!defined($platform)) {
- print "WARNING: Your platform is not recognized. Any platform-specific results will be generated in platform/undefined.\n";
- $platform = "undefined";
-}
-
-my $programName = basename($0);
-my $launchSafariDefault = $launchSafari ? "launch" : "do not launch";
-my $httpDefault = $testHTTP ? "run" : "do not run";
-my $sampleDefault = $runSample ? "run" : "do not run";
-
-my $usage = <<EOF;
-Usage: $programName [options] [testdir|testpath ...]
- --add-platform-exceptions Put new results for non-platform-specific failing tests into the platform-specific results directory
- --complex-text Use the complex text code path for all text (Mac OS X and Windows only)
- -c|--configuration config Set DumpRenderTree build configuration
- -g|--guard-malloc Enable malloc guard
- --exit-after-n-failures N Exit after the first N failures instead of running all tests
- -h|--help Show this help message
- --[no-]http Run (or do not run) http tests (default: $httpDefault)
- --[no-]wait-for-httpd Wait for httpd if some other test session is using it already (same as WEBKIT_WAIT_FOR_HTTPD=1). (default: $shouldWaitForHTTPD)
- -i|--ignore-tests Comma-separated list of directories or tests to ignore
- --iterations n Number of times to run the set of tests (e.g. ABCABCABC)
- --[no-]launch-safari Launch (or do not launch) Safari to display test results (default: $launchSafariDefault)
- -l|--leaks Enable leaks checking
- --[no-]new-test-results Generate results for new tests
- --nthly n Restart DumpRenderTree every n tests (default: $testsPerDumpTool)
- -p|--pixel-tests Enable pixel tests
- --tolerance t Ignore image differences less than this percentage (default: $tolerance)
- --platform Override the detected platform to use for tests and results (default: $platform)
- --port Web server port to use with http tests
- -q|--quiet Less verbose output
- --reset-results Reset ALL results (including pixel tests if --pixel-tests is set)
- -o|--results-directory Output results directory (default: $testResultsDirectory)
- --random Run the tests in a random order
- --repeat-each n Number of times to run each test (e.g. AAABBBCCC)
- --reverse Run the tests in reverse alphabetical order
- --root Path to root tools build
- --[no-]sample-on-timeout Run sample on timeout (default: $sampleDefault) (Mac OS X only)
- -1|--singly Isolate each test case run (implies --nthly 1 --verbose)
- --skipped=[default|ignore|only] Specifies how to treat the Skipped file
- default: Tests/directories listed in the Skipped file are not tested
- ignore: The Skipped file is ignored
- only: Only those tests/directories listed in the Skipped file will be run
- --slowest Report the 10 slowest tests
- --ignore-metrics Ignore metrics in tests
- --[no-]strip-editing-callbacks Remove editing callbacks from expected results
- -t|--threaded Run a concurrent JavaScript thead with each test
- --timeout t Sets the number of seconds before a test times out (default: $timeoutSeconds)
- --valgrind Run DumpRenderTree inside valgrind (Qt/Linux only)
- -v|--verbose More verbose output (overrides --quiet)
- -m|--merge-leak-depth arg Merges leak callStacks and prints the number of unique leaks beneath a callstack depth of arg. Defaults to 5.
- --use-remote-links-to-tests Link to test files within the SVN repository in the results.
-EOF
-
-setConfiguration();
-
-my $getOptionsResult = GetOptions(
- 'add-platform-exceptions' => \$addPlatformExceptions,
- 'complex-text' => \$complexText,
- 'exit-after-n-failures=i' => \$exitAfterNFailures,
- 'guard-malloc|g' => \$guardMalloc,
- 'help|h' => \$showHelp,
- 'http!' => \$testHTTP,
- 'wait-for-httpd!' => \$shouldWaitForHTTPD,
- 'ignore-metrics!' => \$ignoreMetrics,
- 'ignore-tests|i=s' => \$ignoreTests,
- 'iterations=i' => \$iterations,
- 'launch-safari!' => \$launchSafari,
- 'leaks|l' => \$shouldCheckLeaks,
- 'merge-leak-depth|m:5' => \$mergeDepth,
- 'new-test-results!' => \$generateNewResults,
- 'nthly=i' => \$testsPerDumpTool,
- 'pixel-tests|p' => \$pixelTests,
- 'platform=s' => \$platform,
- 'port=i' => \$httpdPort,
- 'quiet|q' => \$quiet,
- 'random' => \$randomizeTests,
- 'repeat-each=i' => \$repeatEach,
- 'reset-results' => \$resetResults,
- 'results-directory|o=s' => \$testResultsDirectory,
- 'reverse' => \$reverseTests,
- 'root=s' => \$root,
- 'sample-on-timeout!' => \$runSample,
- 'singly|1' => sub { $testsPerDumpTool = 1; },
- 'skipped=s' => \&validateSkippedArg,
- 'slowest' => \$report10Slowest,
- 'strip-editing-callbacks!' => \$stripEditingCallbacks,
- 'threaded|t' => \$threaded,
- 'timeout=i' => \$timeoutSeconds,
- 'tolerance=f' => \$tolerance,
- 'use-remote-links-to-tests' => \$useRemoteLinksToTests,
- 'valgrind' => \$useValgrind,
- 'verbose|v' => \$verbose,
-);
-
-if (!$getOptionsResult || $showHelp) {
- print STDERR $usage;
- exit 1;
-}
-
-my $ignoreSkipped = $treatSkipped eq "ignore";
-my $skippedOnly = $treatSkipped eq "only";
-
-my $configuration = configuration();
-
-# We need an environment variable to be able to enable the feature per-slave
-$shouldWaitForHTTPD = $ENV{"WEBKIT_WAIT_FOR_HTTPD"} unless ($shouldWaitForHTTPD);
-$verbose = 1 if $testsPerDumpTool == 1;
-
-if ($shouldCheckLeaks && $testsPerDumpTool > 1000) {
- print STDERR "\nWARNING: Running more than 1000 tests at a time with MallocStackLogging enabled may cause a crash.\n\n";
-}
-
-# Stack logging does not play well with QuickTime on Tiger (rdar://problem/5537157)
-$testMedia = 0 if $shouldCheckLeaks && isTiger();
-
-# Generating remote links causes a lot of unnecessary spew on GTK build bot
-$useRemoteLinksToTests = 0 if isGtk();
-
-setConfigurationProductDir(Cwd::abs_path($root)) if (defined($root));
-my $productDir = productDir();
-$productDir .= "/bin" if isQt();
-$productDir .= "/Programs" if isGtk();
-
-chdirWebKit();
-
-if (!defined($root)) {
- print STDERR "Running build-dumprendertree\n";
-
- local *DEVNULL;
- my ($childIn, $childOut, $childErr);
- if ($quiet) {
- open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null";
- $childOut = ">&DEVNULL";
- $childErr = ">&DEVNULL";
- } else {
- # When not quiet, let the child use our stdout/stderr.
- $childOut = ">&STDOUT";
- $childErr = ">&STDERR";
- }
-
- my @args = argumentsForConfiguration();
- my $buildProcess = open3($childIn, $childOut, $childErr, "WebKitTools/Scripts/build-dumprendertree", @args) or die "Failed to run build-dumprendertree";
- close($childIn);
- waitpid $buildProcess, 0;
- my $buildResult = $?;
- close($childOut);
- close($childErr);
-
- close DEVNULL if ($quiet);
-
- if ($buildResult) {
- print STDERR "Compiling DumpRenderTree failed!\n";
- exit exitStatus($buildResult);
- }
-}
-
-my $dumpToolName = "DumpRenderTree";
-$dumpToolName .= "_debug" if isCygwin() && configurationForVisualStudio() !~ /^Release|Debug_Internal$/;
-my $dumpTool = "$productDir/$dumpToolName";
-die "can't find executable $dumpToolName (looked in $productDir)\n" unless -x $dumpTool;
-
-my $imageDiffTool = "$productDir/ImageDiff";
-$imageDiffTool .= "_debug" if isCygwin() && configurationForVisualStudio() !~ /^Release|Debug_Internal$/;
-die "can't find executable $imageDiffTool (looked in $productDir)\n" if $pixelTests && !-x $imageDiffTool;
-
-checkFrameworks() unless isCygwin();
-
-if (isAppleMacWebKit()) {
- push @INC, $productDir;
- require DumpRenderTreeSupport;
-}
-
-my $layoutTestsName = "LayoutTests";
-my $testDirectory = File::Spec->rel2abs($layoutTestsName);
-my $expectedDirectory = $testDirectory;
-my $platformBaseDirectory = catdir($testDirectory, "platform");
-my $platformTestDirectory = catdir($platformBaseDirectory, $platform);
-my @platformResultHierarchy = buildPlatformResultHierarchy();
-my @platformTestHierarchy = buildPlatformTestHierarchy(@platformResultHierarchy);
-
-$expectedDirectory = $ENV{"WebKitExpectedTestResultsDirectory"} if $ENV{"WebKitExpectedTestResultsDirectory"};
-
-$testResultsDirectory = File::Spec->rel2abs($testResultsDirectory);
-my $testResults = File::Spec->catfile($testResultsDirectory, "results.html");
-
-print "Running tests from $testDirectory\n";
-if ($pixelTests) {
- print "Enabling pixel tests with a tolerance of $tolerance%\n";
- if (isDarwin()) {
- print "WARNING: Temporarily changing the main display color profile:\n";
- print "\tThe colors on your screen will change for the duration of the testing.\n";
- print "\tThis allows the pixel tests to have consistent color values across all machines.\n";
-
- if (isPerianInstalled()) {
- print "WARNING: Perian's QuickTime component is installed and this may affect pixel test results!\n";
- print "\tYou should avoid generating new pixel results in this environment.\n";
- print "\tSee https://bugs.webkit.org/show_bug.cgi?id=22615 for details.\n";
- }
- }
-}
-
-system "ln", "-s", $testDirectory, "/tmp/LayoutTests" unless -x "/tmp/LayoutTests";
-
-my %ignoredFiles = ( "results.html" => 1 );
-my %ignoredDirectories = map { $_ => 1 } qw(platform);
-my %ignoredLocalDirectories = map { $_ => 1 } qw(.svn _svn resources script-tests);
-my %supportedFileExtensions = map { $_ => 1 } qw(html shtml xml xhtml pl php);
-
-if (!checkWebCoreFeatureSupport("MathML", 0)) {
- $ignoredDirectories{'mathml'} = 1;
-}
-
-# FIXME: We should fix webkitperl/features.pm:hasFeature() to do the correct feature detection for Cygwin.
-if (checkWebCoreFeatureSupport("SVG", 0)) {
- $supportedFileExtensions{'svg'} = 1;
-} elsif (isCygwin()) {
- $supportedFileExtensions{'svg'} = 1;
-} else {
- $ignoredLocalDirectories{'svg'} = 1;
-}
-
-if (!$testHTTP) {
- $ignoredDirectories{'http'} = 1;
- $ignoredDirectories{'websocket'} = 1;
-}
-
-if (!$testMedia) {
- $ignoredDirectories{'media'} = 1;
- $ignoredDirectories{'http/tests/media'} = 1;
-}
-
-if (!checkWebCoreFeatureSupport("Accelerated Compositing", 0)) {
- $ignoredDirectories{'compositing'} = 1;
-}
-
-if (!checkWebCoreFeatureSupport("3D Rendering", 0)) {
- $ignoredDirectories{'animations/3d'} = 1;
- $ignoredDirectories{'transforms/3d'} = 1;
-}
-
-if (!checkWebCoreFeatureSupport("3D Canvas", 0)) {
- $ignoredDirectories{'fast/canvas/webgl'} = 1;
-}
-
-if (checkWebCoreFeatureSupport("WML", 0)) {
- $supportedFileExtensions{'wml'} = 1;
-} else {
- $ignoredDirectories{'http/tests/wml'} = 1;
- $ignoredDirectories{'fast/wml'} = 1;
- $ignoredDirectories{'wml'} = 1;
-}
-
-if (!checkWebCoreFeatureSupport("XHTMLMP", 0)) {
- $ignoredDirectories{'fast/xhtmlmp'} = 1;
-}
-
-processIgnoreTests($ignoreTests, "ignore-tests") if $ignoreTests;
-if (!$ignoreSkipped) {
- if (!$skippedOnly || @ARGV == 0) {
- readSkippedFiles("");
- } else {
- # Since readSkippedFiles() appends to @ARGV, we must use a foreach
- # loop so that we only iterate over the original argument list.
- foreach my $argnum (0 .. $#ARGV) {
- readSkippedFiles(shift @ARGV);
- }
- }
-}
-
-my @tests = findTestsToRun();
-
-die "no tests to run\n" if !@tests;
-
-my %counts;
-my %tests;
-my %imagesPresent;
-my %imageDifferences;
-my %durations;
-my $count = 0;
-my $leaksOutputFileNumber = 1;
-my $totalLeaks = 0;
-
-my @toolArgs = ();
-push @toolArgs, "--pixel-tests" if $pixelTests;
-push @toolArgs, "--threaded" if $threaded;
-push @toolArgs, "--complex-text" if $complexText;
-push @toolArgs, "-";
-
-my @diffToolArgs = ();
-push @diffToolArgs, "--tolerance", $tolerance;
-
-$| = 1;
-
-my $dumpToolPID;
-my $isDumpToolOpen = 0;
-my $dumpToolCrashed = 0;
-my $imageDiffToolPID;
-my $isDiffToolOpen = 0;
-
-my $atLineStart = 1;
-my $lastDirectory = "";
-
-my $isHttpdOpen = 0;
-my $isWebSocketServerOpen = 0;
-my $webSocketServerPID = 0;
-my $failedToStartWebSocketServer = 0;
-# wss is disabled until all platforms support pyOpenSSL.
-# my $webSocketSecureServerPID = 0;
-
-sub catch_pipe { $dumpToolCrashed = 1; }
-$SIG{"PIPE"} = "catch_pipe";
-
-print "Testing ", scalar @tests, " test cases";
-print " $iterations times" if ($iterations > 1);
-print ", repeating each test $repeatEach times" if ($repeatEach > 1);
-print ".\n";
-
-my $overallStartTime = time;
-
-my %expectedResultPaths;
-
-my @originalTests = @tests;
-# Add individual test repetitions
-if ($repeatEach > 1) {
- @tests = ();
- foreach my $test (@originalTests) {
- for (my $i = 0; $i < $repeatEach; $i++) {
- push(@tests, $test);
- }
- }
-}
-# Add test set repetitions
-for (my $i = 1; $i < $iterations; $i++) {
- push(@tests, @originalTests);
-}
-
-for my $test (@tests) {
- my $newDumpTool = not $isDumpToolOpen;
- openDumpTool();
-
- my $base = stripExtension($test);
- my $expectedExtension = ".txt";
-
- my $dir = $base;
- $dir =~ s|/[^/]+$||;
-
- if ($newDumpTool || $dir ne $lastDirectory) {
- foreach my $logue (epiloguesAndPrologues($newDumpTool ? "" : $lastDirectory, $dir)) {
- if (isCygwin()) {
- $logue = toWindowsPath($logue);
- } else {
- $logue = canonpath($logue);
- }
- if ($verbose) {
- print "running epilogue or prologue $logue\n";
- }
- print OUT "$logue\n";
- # Throw away output from DumpRenderTree.
- # Once for the test output and once for pixel results (empty)
- while (<IN>) {
- last if /#EOF/;
- }
- while (<IN>) {
- last if /#EOF/;
- }
- }
- }
-
- if ($verbose) {
- print "running $test -> ";
- $atLineStart = 0;
- } elsif (!$quiet) {
- if ($dir ne $lastDirectory) {
- print "\n" unless $atLineStart;
- print "$dir ";
- }
- print ".";
- $atLineStart = 0;
- }
-
- $lastDirectory = $dir;
-
- my $result;
-
- my $startTime = time if $report10Slowest;
-
- # Try to read expected hash file for pixel tests
- my $suffixExpectedHash = "";
- if ($pixelTests && !$resetResults) {
- my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
- if (open EXPECTEDHASH, "$expectedPixelDir/$base-$expectedTag.checksum") {
- my $expectedHash = <EXPECTEDHASH>;
- chomp($expectedHash);
- close EXPECTEDHASH;
-
- # Format expected hash into a suffix string that is appended to the path / URL passed to DRT
- $suffixExpectedHash = "'$expectedHash";
- }
- }
-
- if ($test =~ /^http\//) {
- configureAndOpenHTTPDIfNeeded();
- if ($test !~ /^http\/tests\/local\// && $test !~ /^http\/tests\/ssl\// && $test !~ /^http\/tests\/wml\// && $test !~ /^http\/tests\/media\//) {
- my $path = canonpath($test);
- $path =~ s/^http\/tests\///;
- print OUT "http://127.0.0.1:$httpdPort/$path$suffixExpectedHash\n";
- } elsif ($test =~ /^http\/tests\/ssl\//) {
- my $path = canonpath($test);
- $path =~ s/^http\/tests\///;
- print OUT "https://127.0.0.1:$httpdSSLPort/$path$suffixExpectedHash\n";
- } else {
- my $testPath = "$testDirectory/$test";
- if (isCygwin()) {
- $testPath = toWindowsPath($testPath);
- } else {
- $testPath = canonpath($testPath);
- }
- print OUT "$testPath$suffixExpectedHash\n";
- }
- } elsif ($test =~ /^websocket\//) {
- if ($test =~ /^websocket\/tests\/local\//) {
- my $testPath = "$testDirectory/$test";
- if (isCygwin()) {
- $testPath = toWindowsPath($testPath);
- } else {
- $testPath = canonpath($testPath);
- }
- print OUT "$testPath\n";
- } else {
- if (openWebSocketServerIfNeeded()) {
- my $path = canonpath($test);
- if ($test =~ /^websocket\/tests\/ssl\//) {
- # wss is disabled until all platforms support pyOpenSSL.
- print STDERR "Error: wss is disabled until all platforms support pyOpenSSL.";
- # print OUT "https://127.0.0.1:$webSocketSecurePort/$path\n";
- } else {
- print OUT "http://127.0.0.1:$webSocketPort/$path\n";
- }
- } else {
- # We failed to launch the WebSocket server. Display a useful error message rather than attempting
- # to run tests that expect the server to be available.
- my $errorMessagePath = "$testDirectory/websocket/resources/server-failed-to-start.html";
- $errorMessagePath = isCygwin() ? toWindowsPath($errorMessagePath) : canonpath($errorMessagePath);
- print OUT "$errorMessagePath\n";
- }
- }
- } else {
- my $testPath = "$testDirectory/$test";
- if (isCygwin()) {
- $testPath = toWindowsPath($testPath);
- } else {
- $testPath = canonpath($testPath);
- }
- print OUT "$testPath$suffixExpectedHash\n" if defined $testPath;
- }
-
- # DumpRenderTree is expected to dump two "blocks" to stdout for each test.
- # Each block is terminated by a #EOF on a line by itself.
- # The first block is the output of the test (in text, RenderTree or other formats).
- # The second block is for optional pixel data in PNG format, and may be empty if
- # pixel tests are not being run, or the test does not dump pixels (e.g. text tests).
- my $readResults = readFromDumpToolWithTimer(IN, ERROR);
-
- my $actual = $readResults->{output};
- my $error = $readResults->{error};
-
- $expectedExtension = $readResults->{extension};
- my $expectedFileName = "$base-$expectedTag.$expectedExtension";
-
- my $isText = isTextOnlyTest($actual);
-
- my $expectedDir = expectedDirectoryForTest($base, $isText, $expectedExtension);
- $expectedResultPaths{$base} = "$expectedDir/$expectedFileName";
-
- unless ($readResults->{status} eq "success") {
- my $crashed = $readResults->{status} eq "crashed";
- testCrashedOrTimedOut($test, $base, $crashed, $actual, $error);
- countFinishedTest($test, $base, $crashed ? "crash" : "timedout", 0);
- next;
- }
-
- $durations{$test} = time - $startTime if $report10Slowest;
-
- my $expected;
-
- if (!$resetResults && open EXPECTED, "<", "$expectedDir/$expectedFileName") {
- $expected = "";
- while (<EXPECTED>) {
- next if $stripEditingCallbacks && $_ =~ /^EDITING DELEGATE:/;
- $expected .= $_;
- }
- close EXPECTED;
- }
-
- if ($ignoreMetrics && !$isText && defined $expected) {
- ($actual, $expected) = stripMetrics($actual, $expected);
- }
-
- if ($shouldCheckLeaks && $testsPerDumpTool == 1) {
- print " $test -> ";
- }
-
- my $actualPNG = "";
- my $diffPNG = "";
- my $diffPercentage = 0;
- my $diffResult = "passed";
-
- my $actualHash = "";
- my $expectedHash = "";
- my $actualPNGSize = 0;
-
- while (<IN>) {
- last if /#EOF/;
- if (/ActualHash: ([a-f0-9]{32})/) {
- $actualHash = $1;
- } elsif (/ExpectedHash: ([a-f0-9]{32})/) {
- $expectedHash = $1;
- } elsif (/Content-Length: (\d+)\s*/) {
- $actualPNGSize = $1;
- read(IN, $actualPNG, $actualPNGSize);
- }
- }
-
- if ($verbose && $pixelTests && !$resetResults && $actualPNGSize) {
- if ($actualHash eq "" && $expectedHash eq "") {
- printFailureMessageForTest($test, "WARNING: actual & expected pixel hashes are missing!");
- } elsif ($actualHash eq "") {
- printFailureMessageForTest($test, "WARNING: actual pixel hash is missing!");
- } elsif ($expectedHash eq "") {
- printFailureMessageForTest($test, "WARNING: expected pixel hash is missing!");
- }
- }
- if ($actualPNGSize > 0) {
- my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
-
- if (!$resetResults && ($expectedHash ne $actualHash || ($actualHash eq "" && $expectedHash eq ""))) {
- if (-f "$expectedPixelDir/$base-$expectedTag.png") {
- my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png";
- my $expectedPNG = "";
- open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png";
- read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
-
- openDiffTool();
- print DIFFOUT "Content-Length: $actualPNGSize\n";
- print DIFFOUT $actualPNG;
-
- print DIFFOUT "Content-Length: $expectedPNGSize\n";
- print DIFFOUT $expectedPNG;
-
- while (<DIFFIN>) {
- last if /^error/ || /^diff:/;
- if (/Content-Length: (\d+)\s*/) {
- read(DIFFIN, $diffPNG, $1);
- }
- }
-
- if (/^diff: (.+)% (passed|failed)/) {
- $diffPercentage = $1 + 0;
- $imageDifferences{$base} = $diffPercentage;
- $diffResult = $2;
- }
-
- if (!$diffPercentage) {
- printFailureMessageForTest($test, "pixel hash failed (but pixel test still passes)");
- }
- } elsif ($verbose) {
- printFailureMessageForTest($test, "WARNING: expected image is missing!");
- }
- }
-
- if ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png") {
- mkpath catfile($expectedPixelDir, dirname($base)) if $testDirectory ne $expectedPixelDir;
- writeToFile("$expectedPixelDir/$base-$expectedTag.png", $actualPNG);
- }
-
- if ($actualHash ne "" && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.checksum")) {
- writeToFile("$expectedPixelDir/$base-$expectedTag.checksum", $actualHash);
- }
- }
-
- if (dumpToolDidCrash()) {
- $result = "crash";
- testCrashedOrTimedOut($test, $base, 1, $actual, $error);
- } elsif (!defined $expected) {
- if ($verbose) {
- print "new " . ($resetResults ? "result" : "test") ."\n";
- $atLineStart = 1;
- }
- $result = "new";
-
- if ($generateNewResults || $resetResults) {
- mkpath catfile($expectedDir, dirname($base)) if $testDirectory ne $expectedDir;
- writeToFile("$expectedDir/$expectedFileName", $actual);
- }
- deleteExpectedAndActualResults($base);
- recordActualResultsAndDiff($base, $actual);
- if (!$resetResults) {
- # Always print the file name for new tests, as they will probably need some manual inspection.
- # in verbose mode we already printed the test case, so no need to do it again.
- unless ($verbose) {
- print "\n" unless $atLineStart;
- print "$test -> ";
- }
- my $resultsDir = catdir($expectedDir, dirname($base));
- if ($generateNewResults) {
- print "new (results generated in $resultsDir)\n";
- } else {
- print "new\n";
- }
- $atLineStart = 1;
- }
- } elsif ($actual eq $expected && $diffResult eq "passed") {
- if ($verbose) {
- print "succeeded\n";
- $atLineStart = 1;
- }
- $result = "match";
- deleteExpectedAndActualResults($base);
- } else {
- $result = "mismatch";
-
- my $pixelTestFailed = $pixelTests && $diffPNG && $diffPNG ne "";
- my $testFailed = $actual ne $expected;
-
- my $message = !$testFailed ? "pixel test failed" : "failed";
-
- if (($testFailed || $pixelTestFailed) && $addPlatformExceptions) {
- my $testBase = catfile($testDirectory, $base);
- my $expectedBase = catfile($expectedDir, $base);
- my $testIsMaximallyPlatformSpecific = $testBase =~ m|^\Q$platformTestDirectory\E/|;
- my $expectedResultIsMaximallyPlatformSpecific = $expectedBase =~ m|^\Q$platformTestDirectory\E/|;
- if (!$testIsMaximallyPlatformSpecific && !$expectedResultIsMaximallyPlatformSpecific) {
- mkpath catfile($platformTestDirectory, dirname($base));
- if ($testFailed) {
- my $expectedFile = catfile($platformTestDirectory, "$expectedFileName");
- writeToFile("$expectedFile", $actual);
- }
- if ($pixelTestFailed) {
- my $expectedFile = catfile($platformTestDirectory, "$base-$expectedTag.checksum");
- writeToFile("$expectedFile", $actualHash);
-
- $expectedFile = catfile($platformTestDirectory, "$base-$expectedTag.png");
- writeToFile("$expectedFile", $actualPNG);
- }
- $message .= " (results generated in $platformTestDirectory)";
- }
- }
-
- printFailureMessageForTest($test, $message);
-
- my $dir = "$testResultsDirectory/$base";
- $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
- my $testName = $1;
- mkpath $dir;
-
- deleteExpectedAndActualResults($base);
- recordActualResultsAndDiff($base, $actual);
-
- if ($pixelTestFailed) {
- $imagesPresent{$base} = 1;
-
- writeToFile("$testResultsDirectory/$base-$actualTag.png", $actualPNG);
- writeToFile("$testResultsDirectory/$base-$diffsTag.png", $diffPNG);
-
- my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
- copy("$expectedPixelDir/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png");
-
- open DIFFHTML, ">$testResultsDirectory/$base-$diffsTag.html" or die;
- print DIFFHTML "<html>\n";
- print DIFFHTML "<head>\n";
- print DIFFHTML "<title>$base Image Compare</title>\n";
- print DIFFHTML "<script language=\"Javascript\" type=\"text/javascript\">\n";
- print DIFFHTML "var currentImage = 0;\n";
- print DIFFHTML "var imageNames = new Array(\"Actual\", \"Expected\");\n";
- print DIFFHTML "var imagePaths = new Array(\"$testName-$actualTag.png\", \"$testName-$expectedTag.png\");\n";
- if (-f "$testDirectory/$base-w3c.png") {
- copy("$testDirectory/$base-w3c.png", "$testResultsDirectory/$base-w3c.png");
- print DIFFHTML "imageNames.push(\"W3C\");\n";
- print DIFFHTML "imagePaths.push(\"$testName-w3c.png\");\n";
- }
- print DIFFHTML "function animateImage() {\n";
- print DIFFHTML " var image = document.getElementById(\"animatedImage\");\n";
- print DIFFHTML " var imageText = document.getElementById(\"imageText\");\n";
- print DIFFHTML " image.src = imagePaths[currentImage];\n";
- print DIFFHTML " imageText.innerHTML = imageNames[currentImage] + \" Image\";\n";
- print DIFFHTML " currentImage = (currentImage + 1) % imageNames.length;\n";
- print DIFFHTML " setTimeout('animateImage()',2000);\n";
- print DIFFHTML "}\n";
- print DIFFHTML "</script>\n";
- print DIFFHTML "</head>\n";
- print DIFFHTML "<body onLoad=\"animateImage();\">\n";
- print DIFFHTML "<table>\n";
- if ($diffPercentage) {
- print DIFFHTML "<tr>\n";
- print DIFFHTML "<td>Difference between images: <a href=\"$testName-$diffsTag.png\">$diffPercentage%</a></td>\n";
- print DIFFHTML "</tr>\n";
- }
- print DIFFHTML "<tr>\n";
- print DIFFHTML "<td><a href=\"" . toURL("$testDirectory/$test") . "\">test file</a></td>\n";
- print DIFFHTML "</tr>\n";
- print DIFFHTML "<tr>\n";
- print DIFFHTML "<td id=\"imageText\" style=\"text-weight: bold;\">Actual Image</td>\n";
- print DIFFHTML "</tr>\n";
- print DIFFHTML "<tr>\n";
- print DIFFHTML "<td><img src=\"$testName-$actualTag.png\" id=\"animatedImage\"></td>\n";
- print DIFFHTML "</tr>\n";
- print DIFFHTML "</table>\n";
- print DIFFHTML "</body>\n";
- print DIFFHTML "</html>\n";
- }
- }
-
- if ($error) {
- my $dir = "$testResultsDirectory/$base";
- $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
- mkpath $dir;
-
- writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error);
-
- $counts{error}++;
- push @{$tests{error}}, $test;
- }
-
- countFinishedTest($test, $base, $result, $isText);
-
- # --reset-results does not check pass vs. fail, so exitAfterNFailures makes no sense with --reset-results.
- if ($exitAfterNFailures && !$resetResults) {
- my $passCount = $counts{match} || 0; # $counts{match} will be undefined if we've not yet passed a test (e.g. the first test fails).
- my $failureCount = $count - $passCount; # "Failure" here includes new tests, timeouts, crashes, etc.
- if ($failureCount >= $exitAfterNFailures) {
- print "\nExiting early after $failureCount failures. $count tests run.";
- closeDumpTool();
- last;
- }
- }
-}
-my $totalTestingTime = time - $overallStartTime;
-my $waitTime = getWaitTime();
-if ($waitTime > 0.1) {
- my $normalizedTestingTime = $totalTestingTime - $waitTime;
- printf "\n%0.2fs HTTPD waiting time\n", $waitTime . "";
- printf "%0.2fs normalized testing time", $normalizedTestingTime . "";
-}
-printf "\n%0.2fs total testing time\n", $totalTestingTime . "";
-
-!$isDumpToolOpen || die "Failed to close $dumpToolName.\n";
-
-$isHttpdOpen = !closeHTTPD();
-closeWebSocketServer();
-
-# Because multiple instances of this script are running concurrently we cannot
-# safely delete this symlink.
-# system "rm /tmp/LayoutTests";
-
-# FIXME: Do we really want to check the image-comparison tool for leaks every time?
-if ($isDiffToolOpen && $shouldCheckLeaks) {
- $totalLeaks += countAndPrintLeaks("ImageDiff", $imageDiffToolPID, "$testResultsDirectory/ImageDiff-leaks.txt");
-}
-
-if ($totalLeaks) {
- if ($mergeDepth) {
- parseLeaksandPrintUniqueLeaks();
- } else {
- print "\nWARNING: $totalLeaks total leaks found!\n";
- print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2);
- }
-}
-
-close IN;
-close OUT;
-close ERROR;
-
-if ($report10Slowest) {
- print "\n\nThe 10 slowest tests:\n\n";
- my $count = 0;
- for my $test (sort slowestcmp keys %durations) {
- printf "%0.2f secs: %s\n", $durations{$test}, $test;
- last if ++$count == 10;
- }
-}
-
-print "\n";
-
-if ($skippedOnly && $counts{"match"}) {
- print "The following tests are in the Skipped file (" . File::Spec->abs2rel("$platformTestDirectory/Skipped", $testDirectory) . "), but succeeded:\n";
- foreach my $test (@{$tests{"match"}}) {
- print " $test\n";
- }
-}
-
-if ($resetResults || ($counts{match} && $counts{match} == $count)) {
- print "all $count test cases succeeded\n";
- unlink $testResults;
- exit;
-}
-
-printResults();
-
-mkpath $testResultsDirectory;
-
-open HTML, ">", $testResults or die "Failed to open $testResults. $!";
-print HTML "<html>\n";
-print HTML "<head>\n";
-print HTML "<title>Layout Test Results</title>\n";
-print HTML "</head>\n";
-print HTML "<body>\n";
-
-if ($ignoreMetrics) {
- print HTML "<h4>Tested with metrics ignored.</h4>";
-}
-
-print HTML htmlForResultsSection(@{$tests{mismatch}}, "Tests where results did not match expected results", \&linksForMismatchTest);
-print HTML htmlForResultsSection(@{$tests{timedout}}, "Tests that timed out", \&linksForErrorTest);
-print HTML htmlForResultsSection(@{$tests{crash}}, "Tests that caused the DumpRenderTree tool to crash", \&linksForErrorTest);
-print HTML htmlForResultsSection(@{$tests{error}}, "Tests that had stderr output", \&linksForErrorTest);
-print HTML htmlForResultsSection(@{$tests{new}}, "Tests that had no expected results (probably new)", \&linksForNewTest);
-
-print HTML "</body>\n";
-print HTML "</html>\n";
-close HTML;
-
-my @configurationArgs = argumentsForConfiguration();
-
-if (isGtk()) {
- system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari;
-} elsif (isQt()) {
- unshift @configurationArgs, qw(-graphicssystem raster -style windows);
- if (isCygwin()) {
- $testResults = "/" . toWindowsPath($testResults);
- $testResults =~ s/\\/\//g;
- }
- system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari;
-} elsif (isCygwin()) {
- system "cygstart", $testResults if $launchSafari;
-} else {
- system "WebKitTools/Scripts/run-safari", @configurationArgs, "-NSOpen", $testResults if $launchSafari;
-}
-
-closeCygpaths() if isCygwin();
-
-exit 1;
-
-sub countAndPrintLeaks($$$)
+sub runningOnBuildBot()
{
- my ($dumpToolName, $dumpToolPID, $leaksFilePath) = @_;
-
- print "\n" unless $atLineStart;
- $atLineStart = 1;
-
- # We are excluding the following reported leaks so they don't get in our way when looking for WebKit leaks:
- # This allows us ignore known leaks and only be alerted when new leaks occur. Some leaks are in the old
- # versions of the system frameworks that are being used by the leaks bots. Even though a leak has been
- # fixed, it will be listed here until the bot has been updated with the newer frameworks.
-
- my @typesToExclude = (
- );
-
- my @callStacksToExclude = (
- "Flash_EnforceLocalSecurity" # leaks in Flash plug-in code, rdar://problem/4449747
- );
-
- if (isTiger()) {
- # Leak list for the version of Tiger used on the build bot.
- push @callStacksToExclude, (
- "CFRunLoopRunSpecific \\| malloc_zone_malloc", "CFRunLoopRunSpecific \\| CFAllocatorAllocate ", # leak in CFRunLoopRunSpecific, rdar://problem/4670839
- "CGImageSourceGetPropertiesAtIndex", # leak in ImageIO, rdar://problem/4628809
- "FOGetCoveredUnicodeChars", # leak in ATS, rdar://problem/3943604
- "GetLineDirectionPreference", "InitUnicodeUtilities", # leaks tool falsely reporting leak in CFNotificationCenterAddObserver, rdar://problem/4964790
- "ICCFPrefWrapper::GetPrefDictionary", # leaks in Internet Config. code, rdar://problem/4449794
- "NSHTTPURLProtocol setResponseHeader:", # leak in multipart/mixed-replace handling in Foundation, no Radar, but fixed in Leopard
- "NSURLCache cachedResponseForRequest", # leak in CFURL cache, rdar://problem/4768430
- "PCFragPrepareClosureFromFile", # leak in Code Fragment Manager, rdar://problem/3426998
- "WebCore::Selection::toRange", # bug in 'leaks', rdar://problem/4967949
- "WebCore::SubresourceLoader::create", # bug in 'leaks', rdar://problem/4985806
- "_CFPreferencesDomainDeepCopyDictionary", # leak in CFPreferences, rdar://problem/4220786
- "_objc_msgForward", # leak in NSSpellChecker, rdar://problem/4965278
- "gldGetString", # leak in OpenGL, rdar://problem/5013699
- "_setDefaultUserInfoFromURL", # leak in NSHTTPAuthenticator, rdar://problem/5546453
- "SSLHandshake", # leak in SSL, rdar://problem/5546440
- "SecCertificateCreateFromData", # leak in SSL code, rdar://problem/4464397
- );
- push @typesToExclude, (
- "THRD", # bug in 'leaks', rdar://problem/3387783
- "DRHT", # ditto (endian little hate i)
- );
- }
-
- if (isLeopard()) {
- # Leak list for the version of Leopard used on the build bot.
- push @callStacksToExclude, (
- "CFHTTPMessageAppendBytes", # leak in CFNetwork, rdar://problem/5435912
- "sendDidReceiveDataCallback", # leak in CFNetwork, rdar://problem/5441619
- "_CFHTTPReadStreamReadMark", # leak in CFNetwork, rdar://problem/5441468
- "httpProtocolStart", # leak in CFNetwork, rdar://problem/5468837
- "_CFURLConnectionSendCallbacks", # leak in CFNetwork, rdar://problem/5441600
- "DispatchQTMsg", # leak in QuickTime, PPC only, rdar://problem/5667132
- "QTMovieContentView createVisualContext", # leak in QuickTime, PPC only, rdar://problem/5667132
- "_CopyArchitecturesForJVMVersion", # leak in Java, rdar://problem/5910823
- );
- }
-
- if (isSnowLeopard()) {
- push @callStacksToExclude, (
- "readMakerNoteProps", # <rdar://problem/7156432> leak in ImageIO
- "QTKitMovieControllerView completeUISetup", # <rdar://problem/7155156> leak in QTKit
- );
- }
-
- my $leaksTool = sourceDir() . "/WebKitTools/Scripts/run-leaks";
- my $excludeString = "--exclude-callstack '" . (join "' --exclude-callstack '", @callStacksToExclude) . "'";
- $excludeString .= " --exclude-type '" . (join "' --exclude-type '", @typesToExclude) . "'" if @typesToExclude;
-
- print " ? checking for leaks in $dumpToolName\n";
- my $leaksOutput = `$leaksTool $excludeString $dumpToolPID`;
- my ($count, $bytes) = $leaksOutput =~ /Process $dumpToolPID: (\d+) leaks? for (\d+) total/;
- my ($excluded) = $leaksOutput =~ /(\d+) leaks? excluded/;
-
- my $adjustedCount = $count;
- $adjustedCount -= $excluded if $excluded;
-
- if (!$adjustedCount) {
- print " - no leaks found\n";
- unlink $leaksFilePath;
- return 0;
- } else {
- my $dir = $leaksFilePath;
- $dir =~ s|/[^/]+$|| or die;
- mkpath $dir;
-
- if ($excluded) {
- print " + $adjustedCount leaks ($bytes bytes including $excluded excluded leaks) were found, details in $leaksFilePath\n";
- } else {
- print " + $count leaks ($bytes bytes) were found, details in $leaksFilePath\n";
- }
-
- writeToFile($leaksFilePath, $leaksOutput);
-
- push @leaksFilenames, $leaksFilePath;
- }
-
- return $adjustedCount;
-}
-
-sub writeToFile($$)
-{
- my ($filePath, $contents) = @_;
- open NEWFILE, ">", "$filePath" or die "Could not create $filePath. $!\n";
- print NEWFILE $contents;
- close NEWFILE;
-}
-
-# Break up a path into the directory (with slash) and base name.
-sub splitpath($)
-{
- my ($path) = @_;
-
- my $pathSeparator = "/";
- my $dirname = dirname($path) . $pathSeparator;
- $dirname = "" if $dirname eq "." . $pathSeparator;
-
- return ($dirname, basename($path));
-}
-
-# Sort first by directory, then by file, so all paths in one directory are grouped
-# rather than being interspersed with items from subdirectories.
-# Use numericcmp to sort directory and filenames to make order logical.
-sub pathcmp($$)
-{
- my ($patha, $pathb) = @_;
-
- my ($dira, $namea) = splitpath($patha);
- my ($dirb, $nameb) = splitpath($pathb);
-
- return numericcmp($dira, $dirb) if $dira ne $dirb;
- return numericcmp($namea, $nameb);
-}
-
-# Sort numeric parts of strings as numbers, other parts as strings.
-# Makes 1.33 come after 1.3, which is cool.
-sub numericcmp($$)
-{
- my ($aa, $bb) = @_;
-
- my @a = split /(\d+)/, $aa;
- my @b = split /(\d+)/, $bb;
-
- # Compare one chunk at a time.
- # Each chunk is either all numeric digits, or all not numeric digits.
- while (@a && @b) {
- my $a = shift @a;
- my $b = shift @b;
-
- # Use numeric comparison if chunks are non-equal numbers.
- return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b;
-
- # Use string comparison if chunks are any other kind of non-equal string.
- return $a cmp $b if $a ne $b;
- }
-
- # One of the two is now empty; compare lengths for result in this case.
- return @a <=> @b;
-}
-
-# Sort slowest tests first.
-sub slowestcmp($$)
-{
- my ($testa, $testb) = @_;
-
- my $dura = $durations{$testa};
- my $durb = $durations{$testb};
- return $durb <=> $dura if $dura != $durb;
- return pathcmp($testa, $testb);
-}
-
-sub launchWithEnv(\@\%)
-{
- my ($args, $env) = @_;
-
- # Dump the current environment as perl code and then put it in quotes so it is one parameter.
- my $environmentDumper = Data::Dumper->new([\%{$env}], [qw(*ENV)]);
- $environmentDumper->Indent(0);
- $environmentDumper->Purity(1);
- my $allEnvVars = $environmentDumper->Dump();
- unshift @{$args}, "\"$allEnvVars\"";
-
- my $execScript = File::Spec->catfile(sourceDir(), qw(WebKitTools Scripts execAppWithEnv));
- unshift @{$args}, $execScript;
- return @{$args};
-}
-
-sub resolveAndMakeTestResultsDirectory()
-{
- my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory);
- mkpath $absTestResultsDirectory;
- return $absTestResultsDirectory;
-}
-
-sub openDiffTool()
-{
- return if $isDiffToolOpen;
- return if !$pixelTests;
-
- my %CLEAN_ENV;
- $CLEAN_ENV{MallocStackLogging} = 1 if $shouldCheckLeaks;
- $imageDiffToolPID = open2(\*DIFFIN, \*DIFFOUT, $imageDiffTool, launchWithEnv(@diffToolArgs, %CLEAN_ENV)) or die "unable to open $imageDiffTool\n";
- $isDiffToolOpen = 1;
+ # This is a hack to detect if we're running on the buildbot so we can
+ # pass --verbose to new-run-webkit-tests. This will be removed when we
+ # update the buildbot config to call new-run-webkit-tests explicitly.
+ my %isBuildBotUser = ("apple" => 1, "buildbot" => 1);
+ return $isBuildBotUser{$ENV{"USER"}};
}
-sub openDumpTool()
+sub useNewRunWebKitTests()
{
- return if $isDumpToolOpen;
-
- my %CLEAN_ENV;
-
- # Generic environment variables
- if (defined $ENV{'WEBKIT_TESTFONTS'}) {
- $CLEAN_ENV{WEBKIT_TESTFONTS} = $ENV{'WEBKIT_TESTFONTS'};
- }
-
- $CLEAN_ENV{XML_CATALOG_FILES} = ""; # work around missing /etc/catalog <rdar://problem/4292995>
-
- # Platform spesifics
- if (isLinux()) {
- if (defined $ENV{'DISPLAY'}) {
- $CLEAN_ENV{DISPLAY} = $ENV{'DISPLAY'};
- } else {
- $CLEAN_ENV{DISPLAY} = ":1";
- }
- if (defined $ENV{'XAUTHORITY'}) {
- $CLEAN_ENV{XAUTHORITY} = $ENV{'XAUTHORITY'};
- }
-
- $CLEAN_ENV{HOME} = $ENV{'HOME'};
-
- if (defined $ENV{'LD_LIBRARY_PATH'}) {
- $CLEAN_ENV{LD_LIBRARY_PATH} = $ENV{'LD_LIBRARY_PATH'};
- }
- if (defined $ENV{'DBUS_SESSION_BUS_ADDRESS'}) {
- $CLEAN_ENV{DBUS_SESSION_BUS_ADDRESS} = $ENV{'DBUS_SESSION_BUS_ADDRESS'};
- }
- } elsif (isDarwin()) {
- if (defined $ENV{'DYLD_LIBRARY_PATH'}) {
- $CLEAN_ENV{DYLD_LIBRARY_PATH} = $ENV{'DYLD_LIBRARY_PATH'};
- }
-
- $CLEAN_ENV{DYLD_FRAMEWORK_PATH} = $productDir;
- $CLEAN_ENV{DYLD_INSERT_LIBRARIES} = "/usr/lib/libgmalloc.dylib" if $guardMalloc;
- } elsif (isCygwin()) {
- $CLEAN_ENV{HOMEDRIVE} = $ENV{'HOMEDRIVE'};
- $CLEAN_ENV{HOMEPATH} = $ENV{'HOMEPATH'};
-
- setPathForRunningWebKitApp(\%CLEAN_ENV);
- }
-
- # Port spesifics
- if (isQt()) {
- $CLEAN_ENV{QTWEBKIT_PLUGIN_PATH} = productDir() . "/lib/plugins";
- }
-
- my @args = ($dumpTool, @toolArgs);
- if (isAppleMacWebKit() and !isTiger()) {
- unshift @args, "arch", "-" . architecture();
- }
-
- if ($useValgrind) {
- unshift @args, "valgrind", "--suppressions=$platformBaseDirectory/qt/SuppressedValgrindErrors";
- }
-
- $CLEAN_ENV{MallocStackLogging} = 1 if $shouldCheckLeaks;
-
- $dumpToolPID = open3(\*OUT, \*IN, \*ERROR, launchWithEnv(@args, %CLEAN_ENV)) or die "Failed to start tool: $dumpTool\n";
- $isDumpToolOpen = 1;
- $dumpToolCrashed = 0;
-}
-
-sub closeDumpTool()
-{
- return if !$isDumpToolOpen;
-
- close IN;
- close OUT;
- waitpid $dumpToolPID, 0;
-
- # check for WebCore counter leaks.
- if ($shouldCheckLeaks) {
- while (<ERROR>) {
- print;
- }
- }
- close ERROR;
- $isDumpToolOpen = 0;
-}
-
-sub dumpToolDidCrash()
-{
- return 1 if $dumpToolCrashed;
- return 0 unless $isDumpToolOpen;
- my $pid = waitpid(-1, WNOHANG);
- return 1 if ($pid == $dumpToolPID);
-
- # On Mac OS X, crashing may be significantly delayed by crash reporter.
- return 0 unless isAppleMacWebKit();
-
- return DumpRenderTreeSupport::processIsCrashing($dumpToolPID);
-}
-
-sub configureAndOpenHTTPDIfNeeded()
-{
- return if $isHttpdOpen;
- my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory();
- my $listen = "127.0.0.1:$httpdPort";
- my @args = (
- "-c", "CustomLog \"$absTestResultsDirectory/access_log.txt\" common",
- "-c", "ErrorLog \"$absTestResultsDirectory/error_log.txt\"",
- "-C", "Listen $listen"
- );
-
- my @defaultArgs = getDefaultConfigForTestDirectory($testDirectory);
- @args = (@defaultArgs, @args);
-
- waitForHTTPDLock() if $shouldWaitForHTTPD;
- $isHttpdOpen = openHTTPD(@args);
-}
-
-sub openWebSocketServerIfNeeded()
-{
- return 1 if $isWebSocketServerOpen;
- return 0 if $failedToStartWebSocketServer;
-
- my $webSocketServerPath = "/usr/bin/python";
- my $webSocketPythonPath = "WebKitTools/pywebsocket";
- my $webSocketHandlerDir = "$testDirectory";
- my $webSocketHandlerScanDir = "$testDirectory/websocket/tests";
- my $webSocketHandlerMapFile = "$webSocketHandlerScanDir/handler_map.txt";
- my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem";
- my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory();
- my $logFile = "$absTestResultsDirectory/pywebsocket_log.txt";
-
- my @args = (
- "WebKitTools/pywebsocket/mod_pywebsocket/standalone.py",
- "-p", "$webSocketPort",
- "-d", "$webSocketHandlerDir",
- "-s", "$webSocketHandlerScanDir",
- "-m", "$webSocketHandlerMapFile",
- "-x", "/websocket/tests/cookies",
- "-l", "$logFile",
- "--strict",
- );
- # wss is disabled until all platforms support pyOpenSSL.
- # my @argsSecure = (
- # "WebKitTools/pywebsocket/mod_pywebsocket/standalone.py",
- # "-p", "$webSocketSecurePort",
- # "-d", "$webSocketHandlerDir",
- # "-t",
- # "-k", "$sslCertificate",
- # "-c", "$sslCertificate",
- # );
-
- $ENV{"PYTHONPATH"} = $webSocketPythonPath;
- $webSocketServerPID = open3(\*WEBSOCKETSERVER_IN, \*WEBSOCKETSERVER_OUT, \*WEBSOCKETSERVER_ERR, $webSocketServerPath, @args);
- # wss is disabled until all platforms support pyOpenSSL.
- # $webSocketSecureServerPID = open3(\*WEBSOCKETSECURESERVER_IN, \*WEBSOCKETSECURESERVER_OUT, \*WEBSOCKETSECURESERVER_ERR, $webSocketServerPath, @argsSecure);
- # my @listen = ("http://127.0.0.1:$webSocketPort", "https://127.0.0.1:$webSocketSecurePort");
- my @listen = ("http://127.0.0.1:$webSocketPort");
- for (my $i = 0; $i < @listen; $i++) {
- my $retryCount = 10;
- while (system("/usr/bin/curl -k -q --silent --stderr - --output /dev/null $listen[$i]") && $retryCount) {
- sleep 1;
- --$retryCount;
- }
- unless ($retryCount) {
- print STDERR "Timed out waiting for WebSocketServer to start.\n";
- $failedToStartWebSocketServer = 1;
- return 0;
- }
- }
-
- $isWebSocketServerOpen = 1;
- return 1;
-}
-
-sub closeWebSocketServer()
-{
- return if !$isWebSocketServerOpen;
-
- close WEBSOCKETSERVER_IN;
- close WEBSOCKETSERVER_OUT;
- close WEBSOCKETSERVER_ERR;
- kill 15, $webSocketServerPID;
-
- # wss is disabled until all platforms support pyOpenSSL.
- # close WEBSOCKETSECURESERVER_IN;
- # close WEBSOCKETSECURESERVER_OUT;
- # close WEBSOCKETSECURESERVER_ERR;
- # kill 15, $webSocketSecureServerPID;
-
- $isWebSocketServerOpen = 0;
-}
-
-sub fileNameWithNumber($$)
-{
- my ($base, $number) = @_;
- return "$base$number" if ($number > 1);
- return $base;
-}
-
-sub processIgnoreTests($$)
-{
- my @ignoreList = split(/\s*,\s*/, shift);
- my $listName = shift;
-
- my $disabledSuffix = "-disabled";
-
- my $addIgnoredDirectories = sub {
- return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
- $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)} = 1;
- return @_;
- };
- foreach my $item (@ignoreList) {
- my $path = catfile($testDirectory, $item);
- if (-d $path) {
- $ignoredDirectories{$item} = 1;
- find({ preprocess => $addIgnoredDirectories, wanted => sub {} }, $path);
- }
- elsif (-f $path) {
- $ignoredFiles{$item} = 1;
- } elsif (-f $path . $disabledSuffix) {
- # The test is disabled, so do nothing.
- } else {
- print "$listName list contained '$item', but no file of that name could be found\n";
- }
- }
-}
-
-sub stripExtension($)
-{
- my ($test) = @_;
-
- $test =~ s/\.[a-zA-Z]+$//;
- return $test;
-}
-
-sub isTextOnlyTest($)
-{
- my ($actual) = @_;
- my $isText;
- if ($actual =~ /^layer at/ms) {
- $isText = 0;
- } else {
- $isText = 1;
- }
- return $isText;
-}
-
-sub expectedDirectoryForTest($;$;$)
-{
- my ($base, $isText, $expectedExtension) = @_;
-
- my @directories = @platformResultHierarchy;
- push @directories, map { catdir($platformBaseDirectory, $_) } qw(mac-snowleopard mac) if isCygwin();
- push @directories, $expectedDirectory;
-
- # If we already have expected results, just return their location.
- foreach my $directory (@directories) {
- return $directory if (-f "$directory/$base-$expectedTag.$expectedExtension");
- }
-
- # For cross-platform tests, text-only results should go in the cross-platform directory,
- # while render tree dumps should go in the least-specific platform directory.
- return $isText ? $expectedDirectory : $platformResultHierarchy[$#platformResultHierarchy];
-}
-
-sub countFinishedTest($$$$)
-{
- my ($test, $base, $result, $isText) = @_;
-
- if (($count + 1) % $testsPerDumpTool == 0 || $count == $#tests) {
- if ($shouldCheckLeaks) {
- my $fileName;
- if ($testsPerDumpTool == 1) {
- $fileName = "$testResultsDirectory/$base-leaks.txt";
- } else {
- $fileName = "$testResultsDirectory/" . fileNameWithNumber($dumpToolName, $leaksOutputFileNumber) . "-leaks.txt";
- }
- my $leakCount = countAndPrintLeaks($dumpToolName, $dumpToolPID, $fileName);
- $totalLeaks += $leakCount;
- $leaksOutputFileNumber++ if ($leakCount);
- }
-
- closeDumpTool();
- }
-
- $count++;
- $counts{$result}++;
- push @{$tests{$result}}, $test;
-}
-
-sub testCrashedOrTimedOut($$$$$)
-{
- my ($test, $base, $didCrash, $actual, $error) = @_;
-
- printFailureMessageForTest($test, $didCrash ? "crashed" : "timed out");
-
- sampleDumpTool() unless $didCrash;
-
- my $dir = "$testResultsDirectory/$base";
- $dir =~ s|/([^/]+)$|| or die "Failed to find test name from base\n";
- mkpath $dir;
-
- deleteExpectedAndActualResults($base);
-
- if (defined($error) && length($error)) {
- writeToFile("$testResultsDirectory/$base-$errorTag.txt", $error);
- }
-
- recordActualResultsAndDiff($base, $actual);
-
- kill 9, $dumpToolPID unless $didCrash;
-
- closeDumpTool();
-}
-
-sub printFailureMessageForTest($$)
-{
- my ($test, $description) = @_;
-
- unless ($verbose) {
- print "\n" unless $atLineStart;
- print "$test -> ";
- }
- print "$description\n";
- $atLineStart = 1;
-}
-
-my %cygpaths = ();
-
-sub openCygpathIfNeeded($)
-{
- my ($options) = @_;
-
- return unless isCygwin();
- return $cygpaths{$options} if $cygpaths{$options} && $cygpaths{$options}->{"open"};
-
- local (*CYGPATHIN, *CYGPATHOUT);
- my $pid = open2(\*CYGPATHIN, \*CYGPATHOUT, "cygpath -f - $options");
- my $cygpath = {
- "pid" => $pid,
- "in" => *CYGPATHIN,
- "out" => *CYGPATHOUT,
- "open" => 1
- };
-
- $cygpaths{$options} = $cygpath;
-
- return $cygpath;
-}
-
-sub closeCygpaths()
-{
- return unless isCygwin();
-
- foreach my $cygpath (values(%cygpaths)) {
- close $cygpath->{"in"};
- close $cygpath->{"out"};
- waitpid($cygpath->{"pid"}, 0);
- $cygpath->{"open"} = 0;
-
- }
-}
-
-sub convertPathUsingCygpath($$)
-{
- my ($path, $options) = @_;
-
- my $cygpath = openCygpathIfNeeded($options);
- local *inFH = $cygpath->{"in"};
- local *outFH = $cygpath->{"out"};
- print outFH $path . "\n";
- my $convertedPath = <inFH>;
- chomp($convertedPath) if defined $convertedPath;
- return $convertedPath;
-}
-
-sub toWindowsPath($)
-{
- my ($path) = @_;
- return unless isCygwin();
-
- return convertPathUsingCygpath($path, "-w");
-}
-
-sub toURL($)
-{
- my ($path) = @_;
-
- if ($useRemoteLinksToTests) {
- my $relativePath = File::Spec->abs2rel($path, $testDirectory);
-
- # If the file is below the test directory then convert it into a link to the file in SVN
- if ($relativePath !~ /^\.\.\//) {
- my $revision = svnRevisionForDirectory($testDirectory);
- my $svnPath = pathRelativeToSVNRepositoryRootForPath($path);
- return "http://trac.webkit.org/export/$revision/$svnPath";
- }
- }
-
- return $path unless isCygwin();
-
- return "file:///" . convertPathUsingCygpath($path, "-m");
-}
-
-sub validateSkippedArg($$;$)
-{
- my ($option, $value, $value2) = @_;
- my %validSkippedValues = map { $_ => 1 } qw(default ignore only);
- $value = lc($value);
- die "Invalid argument '" . $value . "' for option $option" unless $validSkippedValues{$value};
- $treatSkipped = $value;
-}
-
-sub htmlForResultsSection(\@$&)
-{
- my ($tests, $description, $linkGetter) = @_;
-
- my @html = ();
- return join("\n", @html) unless @{$tests};
-
- push @html, "<p>$description:</p>";
- push @html, "<table>";
- foreach my $test (@{$tests}) {
- push @html, "<tr>";
- push @html, "<td><a href=\"" . toURL("$testDirectory/$test") . "\">$test</a></td>";
- foreach my $link (@{&{$linkGetter}($test)}) {
- push @html, "<td><a href=\"$link->{href}\">$link->{text}</a></td>";
- }
- push @html, "</tr>";
- }
- push @html, "</table>";
-
- return join("\n", @html);
-}
-
-sub linksForExpectedAndActualResults($)
-{
- my ($base) = @_;
-
- my @links = ();
-
- return \@links unless -s "$testResultsDirectory/$base-$diffsTag.txt";
-
- my $expectedResultPath = $expectedResultPaths{$base};
- my ($expectedResultFileName, $expectedResultsDirectory, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
-
- push @links, { href => "$base-$expectedTag$expectedResultExtension", text => "expected" };
- push @links, { href => "$base-$actualTag$expectedResultExtension", text => "actual" };
- push @links, { href => "$base-$diffsTag.txt", text => "diff" };
- push @links, { href => "$base-$prettyDiffTag.html", text => "pretty diff" };
-
- return \@links;
-}
-
-sub linksForMismatchTest
-{
- my ($test) = @_;
-
- my @links = ();
-
- my $base = stripExtension($test);
-
- push @links, @{linksForExpectedAndActualResults($base)};
- return \@links unless $pixelTests && $imagesPresent{$base};
-
- push @links, { href => "$base-$expectedTag.png", text => "expected image" };
- push @links, { href => "$base-$diffsTag.html", text => "image diffs" };
- push @links, { href => "$base-$diffsTag.png", text => "$imageDifferences{$base}%" };
-
- return \@links;
-}
-
-sub linksForErrorTest
-{
- my ($test) = @_;
-
- my @links = ();
-
- my $base = stripExtension($test);
-
- push @links, @{linksForExpectedAndActualResults($base)};
- push @links, { href => "$base-$errorTag.txt", text => "stderr" };
-
- return \@links;
-}
-
-sub linksForNewTest
-{
- my ($test) = @_;
-
- my @links = ();
-
- my $base = stripExtension($test);
-
- my $expectedResultPath = $expectedResultPaths{$base};
- my ($expectedResultFileName, $expectedResultsDirectory, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
-
- push @links, { href => "$base-$actualTag$expectedResultExtension", text => "result" };
- if ($pixelTests && $imagesPresent{$base}) {
- push @links, { href => "$base-$expectedTag.png", text => "image" };
- }
-
- return \@links;
-}
-
-sub deleteExpectedAndActualResults($)
-{
- my ($base) = @_;
-
- unlink "$testResultsDirectory/$base-$actualTag.txt";
- unlink "$testResultsDirectory/$base-$diffsTag.txt";
- unlink "$testResultsDirectory/$base-$errorTag.txt";
-}
-
-sub recordActualResultsAndDiff($$)
-{
- my ($base, $actualResults) = @_;
-
- return unless defined($actualResults) && length($actualResults);
-
- my $expectedResultPath = $expectedResultPaths{$base};
- my ($expectedResultFileNameMinusExtension, $expectedResultDirectoryPath, $expectedResultExtension) = fileparse($expectedResultPath, qr{\.[^.]+$});
- my $actualResultsPath = "$testResultsDirectory/$base-$actualTag$expectedResultExtension";
- my $copiedExpectedResultsPath = "$testResultsDirectory/$base-$expectedTag$expectedResultExtension";
-
- mkpath(dirname($actualResultsPath));
- writeToFile("$actualResultsPath", $actualResults);
-
- if (-f $expectedResultPath) {
- copy("$expectedResultPath", "$copiedExpectedResultsPath");
- } else {
- open EMPTY, ">$copiedExpectedResultsPath";
- close EMPTY;
- }
-
- my $diffOuputBasePath = "$testResultsDirectory/$base";
- my $diffOutputPath = "$diffOuputBasePath-$diffsTag.txt";
- system "diff -u \"$copiedExpectedResultsPath\" \"$actualResultsPath\" > \"$diffOutputPath\"";
-
- my $prettyDiffOutputPath = "$diffOuputBasePath-$prettyDiffTag.html";
- my $prettyPatchPath = "BugsSite/PrettyPatch/";
- my $prettifyPath = "$prettyPatchPath/prettify.rb";
- system "ruby -I \"$prettyPatchPath\" \"$prettifyPath\" \"$diffOutputPath\" > \"$prettyDiffOutputPath\"";
-}
-
-sub buildPlatformResultHierarchy()
-{
- mkpath($platformTestDirectory) if ($platform eq "undefined" && !-d "$platformTestDirectory");
-
- my @platforms;
- if ($platform =~ /^mac-/) {
- my $i;
- for ($i = 0; $i < @macPlatforms; $i++) {
- last if $macPlatforms[$i] eq $platform;
- }
- for (; $i < @macPlatforms; $i++) {
- push @platforms, $macPlatforms[$i];
- }
- } elsif ($platform =~ /^qt-/) {
- push @platforms, $platform;
- push @platforms, "qt";
- } else {
- @platforms = $platform;
- }
-
- my @hierarchy;
- for (my $i = 0; $i < @platforms; $i++) {
- my $scoped = catdir($platformBaseDirectory, $platforms[$i]);
- push(@hierarchy, $scoped) if (-d $scoped);
- }
-
- return @hierarchy;
-}
-
-sub buildPlatformTestHierarchy(@)
-{
- my (@platformHierarchy) = @_;
- return @platformHierarchy if (@platformHierarchy < 2);
-
- return ($platformHierarchy[0], $platformHierarchy[$#platformHierarchy]);
-}
-
-sub epiloguesAndPrologues($$)
-{
- my ($lastDirectory, $directory) = @_;
- my @lastComponents = split('/', $lastDirectory);
- my @components = split('/', $directory);
-
- while (@lastComponents) {
- if (!defined($components[0]) || $lastComponents[0] ne $components[0]) {
- last;
- }
- shift @components;
- shift @lastComponents;
- }
-
- my @result;
- my $leaving = $lastDirectory;
- foreach (@lastComponents) {
- my $epilogue = $leaving . "/resources/run-webkit-tests-epilogue.html";
- foreach (@platformResultHierarchy) {
- push @result, catdir($_, $epilogue) if (stat(catdir($_, $epilogue)));
- }
- push @result, catdir($testDirectory, $epilogue) if (stat(catdir($testDirectory, $epilogue)));
- $leaving =~ s|(^\|/)[^/]+$||;
- }
-
- my $entering = $leaving;
- foreach (@components) {
- $entering .= '/' . $_;
- my $prologue = $entering . "/resources/run-webkit-tests-prologue.html";
- push @result, catdir($testDirectory, $prologue) if (stat(catdir($testDirectory, $prologue)));
- foreach (reverse @platformResultHierarchy) {
- push @result, catdir($_, $prologue) if (stat(catdir($_, $prologue)));
- }
- }
- return @result;
-}
-
-sub parseLeaksandPrintUniqueLeaks()
-{
- return unless @leaksFilenames;
-
- my $mergedFilenames = join " ", @leaksFilenames;
- my $parseMallocHistoryTool = sourceDir() . "/WebKitTools/Scripts/parse-malloc-history";
-
- open MERGED_LEAKS, "cat $mergedFilenames | $parseMallocHistoryTool --merge-depth $mergeDepth - |" ;
- my @leakLines = <MERGED_LEAKS>;
- close MERGED_LEAKS;
-
- my $uniqueLeakCount = 0;
- my $totalBytes;
- foreach my $line (@leakLines) {
- ++$uniqueLeakCount if ($line =~ /^(\d*)\scalls/);
- $totalBytes = $1 if $line =~ /^total\:\s(.*)\s\(/;
- }
-
- print "\nWARNING: $totalLeaks total leaks found for a total of $totalBytes!\n";
- print "WARNING: $uniqueLeakCount unique leaks found!\n";
- print "See above for individual leaks results.\n" if ($leaksOutputFileNumber > 2);
-
-}
-
-sub extensionForMimeType($)
-{
- my ($mimeType) = @_;
-
- if ($mimeType eq "application/x-webarchive") {
- return "webarchive";
- } elsif ($mimeType eq "application/pdf") {
- return "pdf";
- }
- return "txt";
-}
-
-# Read up to the first #EOF (the content block of the test), or until detecting crashes or timeouts.
-sub readFromDumpToolWithTimer(**)
-{
- my ($fhIn, $fhError) = @_;
-
- setFileHandleNonBlocking($fhIn, 1);
- setFileHandleNonBlocking($fhError, 1);
-
- my $maximumSecondsWithoutOutput = $timeoutSeconds;
- $maximumSecondsWithoutOutput *= 10 if $guardMalloc;
- my $microsecondsToWaitBeforeReadingAgain = 1000;
-
- my $timeOfLastSuccessfulRead = time;
-
- my @output = ();
- my @error = ();
- my $status = "success";
- my $mimeType = "text/plain";
- # We don't have a very good way to know when the "headers" stop
- # and the content starts, so we use this as a hack:
- my $haveSeenContentType = 0;
- my $haveSeenEofIn = 0;
- my $haveSeenEofError = 0;
-
- while (1) {
- if (time - $timeOfLastSuccessfulRead > $maximumSecondsWithoutOutput) {
- $status = dumpToolDidCrash() ? "crashed" : "timedOut";
- last;
- }
-
- # Once we've seen the EOF, we must not read anymore.
- my $lineIn = readline($fhIn) unless $haveSeenEofIn;
- my $lineError = readline($fhError) unless $haveSeenEofError;
- if (!defined($lineIn) && !defined($lineError)) {
- last if ($haveSeenEofIn && $haveSeenEofError);
-
- if ($! != EAGAIN) {
- $status = "crashed";
- last;
- }
-
- # No data ready
- usleep($microsecondsToWaitBeforeReadingAgain);
- next;
- }
-
- $timeOfLastSuccessfulRead = time;
-
- if (defined($lineIn)) {
- if (!$haveSeenContentType && $lineIn =~ /^Content-Type: (\S+)$/) {
- $mimeType = $1;
- $haveSeenContentType = 1;
- } elsif ($lineIn =~ /#EOF/) {
- $haveSeenEofIn = 1;
- } else {
- push @output, $lineIn;
- }
- }
- if (defined($lineError)) {
- if ($lineError =~ /#EOF/) {
- $haveSeenEofError = 1;
- } else {
- push @error, $lineError;
- }
- }
- }
-
- setFileHandleNonBlocking($fhIn, 0);
- setFileHandleNonBlocking($fhError, 0);
- return {
- output => join("", @output),
- error => join("", @error),
- status => $status,
- mimeType => $mimeType,
- extension => extensionForMimeType($mimeType)
- };
-}
-
-sub setFileHandleNonBlocking(*$)
-{
- my ($fh, $nonBlocking) = @_;
-
- my $flags = fcntl($fh, F_GETFL, 0) or die "Couldn't get filehandle flags";
-
- if ($nonBlocking) {
- $flags |= O_NONBLOCK;
- } else {
- $flags &= ~O_NONBLOCK;
- }
-
- fcntl($fh, F_SETFL, $flags) or die "Couldn't set filehandle flags";
-
- return 1;
-}
-
-sub sampleDumpTool()
-{
- return unless isAppleMacWebKit();
- return unless $runSample;
-
- my $outputDirectory = "$ENV{HOME}/Library/Logs/DumpRenderTree";
- -d $outputDirectory or mkdir $outputDirectory;
-
- my $outputFile = "$outputDirectory/HangReport.txt";
- system "/usr/bin/sample", $dumpToolPID, qw(10 10 -file), $outputFile;
-}
-
-sub stripMetrics($$)
-{
- my ($actual, $expected) = @_;
-
- foreach my $result ($actual, $expected) {
- $result =~ s/at \(-?[0-9]+,-?[0-9]+\) *//g;
- $result =~ s/size -?[0-9]+x-?[0-9]+ *//g;
- $result =~ s/text run width -?[0-9]+: //g;
- $result =~ s/text run width -?[0-9]+ [a-zA-Z ]+: //g;
- $result =~ s/RenderButton {BUTTON} .*/RenderButton {BUTTON}/g;
- $result =~ s/RenderImage {INPUT} .*/RenderImage {INPUT}/g;
- $result =~ s/RenderBlock {INPUT} .*/RenderBlock {INPUT}/g;
- $result =~ s/RenderTextControl {INPUT} .*/RenderTextControl {INPUT}/g;
- $result =~ s/\([0-9]+px/px/g;
- $result =~ s/ *" *\n +" */ /g;
- $result =~ s/" +$/"/g;
-
- $result =~ s/- /-/g;
- $result =~ s/\n( *)"\s+/\n$1"/g;
- $result =~ s/\s+"\n/"\n/g;
- $result =~ s/scrollWidth [0-9]+/scrollWidth/g;
- $result =~ s/scrollHeight [0-9]+/scrollHeight/g;
- }
-
- return ($actual, $expected);
-}
-
-sub fileShouldBeIgnored
-{
- my ($filePath) = @_;
- foreach my $ignoredDir (keys %ignoredDirectories) {
- if ($filePath =~ m/^$ignoredDir/) {
- return 1;
- }
- }
+ # Change this check to control which platforms use
+ # new-run-webkit-tests by default.
return 0;
}
-sub readSkippedFiles($)
-{
- my ($constraintPath) = @_;
-
- foreach my $level (@platformTestHierarchy) {
- if (open SKIPPED, "<", "$level/Skipped") {
- if ($verbose) {
- my ($dir, $name) = splitpath($level);
- print "Skipped tests in $name:\n";
- }
-
- while (<SKIPPED>) {
- my $skipped = $_;
- chomp $skipped;
- $skipped =~ s/^[ \n\r]+//;
- $skipped =~ s/[ \n\r]+$//;
- if ($skipped && $skipped !~ /^#/) {
- if ($skippedOnly) {
- if (!fileShouldBeIgnored($skipped)) {
- if (!$constraintPath) {
- # Always add $skipped since no constraint path was specified on the command line.
- push(@ARGV, $skipped);
- } elsif ($skipped =~ /^($constraintPath)/) {
- # Add $skipped only if it matches the current path constraint, e.g.,
- # "--skipped=only dir1" with "dir1/file1.html" on the skipped list.
- push(@ARGV, $skipped);
- } elsif ($constraintPath =~ /^($skipped)/) {
- # Add current path constraint if it is more specific than the skip list entry,
- # e.g., "--skipped=only dir1/dir2/dir3" with "dir1" on the skipped list.
- push(@ARGV, $constraintPath);
- }
- } elsif ($verbose) {
- print " $skipped\n";
- }
- } else {
- if ($verbose) {
- print " $skipped\n";
- }
- processIgnoreTests($skipped, "Skipped");
- }
- }
- }
- close SKIPPED;
- }
- }
-}
-
-my @testsToRun;
-
-sub directoryFilter
-{
- return () if exists $ignoredLocalDirectories{basename($File::Find::dir)};
- return () if exists $ignoredDirectories{File::Spec->abs2rel($File::Find::dir, $testDirectory)};
- return @_;
-}
-
-sub fileFilter
-{
- my $filename = $_;
- if ($filename =~ /\.([^.]+)$/) {
- if (exists $supportedFileExtensions{$1}) {
- my $path = File::Spec->abs2rel(catfile($File::Find::dir, $filename), $testDirectory);
- push @testsToRun, $path if !exists $ignoredFiles{$path};
- }
- }
-}
-
-sub findTestsToRun
-{
- @testsToRun = ();
-
- for my $test (@ARGV) {
- $test =~ s/^($layoutTestsName|$testDirectory)\///;
- my $fullPath = catfile($testDirectory, $test);
- if (file_name_is_absolute($test)) {
- print "can't run test $test outside $testDirectory\n";
- } elsif (-f $fullPath) {
- my ($filename, $pathname, $fileExtension) = fileparse($test, qr{\.[^.]+$});
- if (!exists $supportedFileExtensions{substr($fileExtension, 1)}) {
- print "test $test does not have a supported extension\n";
- } elsif ($testHTTP || $pathname !~ /^http\//) {
- push @testsToRun, $test;
- }
- } elsif (-d $fullPath) {
- find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $fullPath);
- for my $level (@platformTestHierarchy) {
- my $platformPath = catfile($level, $test);
- find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $platformPath) if (-d $platformPath);
- }
- } else {
- print "test $test not found\n";
- }
- }
-
- if (!scalar @ARGV) {
- find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $testDirectory);
- for my $level (@platformTestHierarchy) {
- find({ preprocess => \&directoryFilter, wanted => \&fileFilter }, $level);
- }
- }
-
- # Remove duplicate tests
- @testsToRun = keys %{{ map { $_ => 1 } @testsToRun }};
+my $harnessName = "old-run-webkit-tests";
- @testsToRun = sort pathcmp @testsToRun;
-
- # We need to minimize the time when Apache and WebSocketServer is locked by tests
- # so run them last if no explicit order was specified in the argument list.
- if (!scalar @ARGV) {
- my @httpTests;
- my @websocketTests;
- my @otherTests;
- foreach my $test (@testsToRun) {
- if ($test =~ /^http\//) {
- push(@httpTests, $test);
- } elsif ($test =~ /^websocket\//) {
- push(@websocketTests, $test);
- } else {
- push(@otherTests, $test);
- }
- }
- @testsToRun = (@otherTests, @httpTests, @websocketTests);
+if (useNewRunWebKitTests()) {
+ $harnessName = "new-run-webkit-tests";
+ if (runningOnBuildBot()) {
+ push(@ARGV, "--verbose");
+ # old-run-webkit-tests treats --results-directory as $CWD relative.
+ # new-run-webkit-tests treats --results-directory as build directory relative.
+ # Override the passed in --results-directory by appending a new one
+ # (later arguments override earlier ones in Python's optparse).
+ push(@ARGV, "--results-directory");
+ # The buildbot always uses $SRCDIR/layout-test-results, hardcode it:
+ push(@ARGV, sourceDir() . "/layout-test-results");
}
-
- # Reverse the tests
- @testsToRun = reverse @testsToRun if $reverseTests;
-
- # Shuffle the array
- @testsToRun = shuffle(@testsToRun) if $randomizeTests;
-
- return @testsToRun;
}
-sub printResults
-{
- my %text = (
- match => "succeeded",
- mismatch => "had incorrect layout",
- new => "were new",
- timedout => "timed out",
- crash => "crashed",
- error => "had stderr output"
- );
-
- for my $type ("match", "mismatch", "new", "timedout", "crash", "error") {
- my $typeCount = $counts{$type};
- next unless $typeCount;
- my $typeText = $text{$type};
- my $message;
- if ($typeCount == 1) {
- $typeText =~ s/were/was/;
- $message = sprintf "1 test case (%d%%) %s\n", 1 * 100 / $count, $typeText;
- } else {
- $message = sprintf "%d test cases (%d%%) %s\n", $typeCount, $typeCount * 100 / $count, $typeText;
- }
- $message =~ s-\(0%\)-(<1%)-;
- print $message;
- }
-}
+my $harnessPath = sprintf("%s/%s", relativeScriptsDir(), $harnessName);
+exec $harnessPath ($harnessPath, @ARGV) or die "Failed to execute $harnessPath";
diff --git a/WebKitTools/Scripts/run-webkit-websocketserver b/WebKitTools/Scripts/run-webkit-websocketserver
index 64a724d..06f9079 100755
--- a/WebKitTools/Scripts/run-webkit-websocketserver
+++ b/WebKitTools/Scripts/run-webkit-websocketserver
@@ -64,18 +64,19 @@ exit 0;
sub openWebSocketServer()
{
my $webSocketServerPath = "/usr/bin/python";
- my $webSocketPythonPath = "$srcDir/WebKitTools/pywebsocket";
+ my $webSocketPythonPath = "$srcDir/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket";
my $webSocketHandlerDir = "$testDirectory";
my $webSocketHandlerScanDir = "$testDirectory/websocket/tests";
my $webSocketHandlerMapFile = "$webSocketHandlerScanDir/handler_map.txt";
my @args = (
- "$srcDir/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py",
- "-p", "$webSocketPort",
- "-d", "$webSocketHandlerDir",
- "-s", "$webSocketHandlerScanDir",
- "-m", "$webSocketHandlerMapFile",
- "-x", "/websocket/tests/cookies",
+ "$srcDir/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py",
+ "--server-host", "127.0.0.1",
+ "--port", "$webSocketPort",
+ "--document-root", "$webSocketHandlerDir",
+ "--scan-dir", "$webSocketHandlerScanDir",
+ "--websock-handlers-map-file", "$webSocketHandlerMapFile",
+ "--cgi-paths", "/websocket/tests",
);
$ENV{"PYTHONPATH"} = $webSocketPythonPath;
diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply
index f586211..61d193d 100755
--- a/WebKitTools/Scripts/svn-apply
+++ b/WebKitTools/Scripts/svn-apply
@@ -310,6 +310,7 @@ sub handleGitBinaryChange($$)
sub isDirectoryEmptyForRemoval($)
{
my ($dir) = @_;
+ return 1 unless -d $dir;
my $directoryIsEmpty = 1;
opendir DIR, $dir or die "Could not open '$dir' to list files: $?";
for (my $item = readdir DIR; $item && $directoryIsEmpty; $item = readdir DIR) {
@@ -481,6 +482,12 @@ sub scmRemove($)
close SVN;
print $svnOutput if $svnOutput;
} elsif (isGit()) {
- system("git", "rm", "--force", $path) == 0 or die "Failed to git rm --force $path.";
+ # Git removes a directory if it becomes empty when the last file it contains is
+ # removed by `git rm`. In svn-apply this can happen when a directory is being
+ # removed in a patch, and all of the files inside of the directory are removed
+ # before attemping to remove the directory itself. In this case, Git will have
+ # already deleted the directory and `git rm` would exit with an error claiming
+ # there was no file. The --ignore-unmatch switch gracefully handles this case.
+ system("git", "rm", "--force", "--ignore-unmatch", $path) == 0 or die "Failed to git rm --force --ignore-unmatch $path.";
}
}
diff --git a/WebKitTools/Scripts/svn-create-patch b/WebKitTools/Scripts/svn-create-patch
index 768a8ed..5aead2e 100755
--- a/WebKitTools/Scripts/svn-create-patch
+++ b/WebKitTools/Scripts/svn-create-patch
@@ -56,12 +56,14 @@ use Time::gmtime;
use VCSUtils;
sub binarycmp($$);
+sub diffOptionsForFile($);
sub findBaseUrl($);
sub findMimeType($;$);
sub findModificationType($);
sub findSourceFileAndRevision($);
sub generateDiff($$);
sub generateFileList($\%);
+sub hunkHeaderLineRegExForFile($);
sub isBinaryMimeType($);
sub manufacturePatchForAdditionWithHistory($);
sub numericcmp($$);
@@ -99,9 +101,16 @@ for my $path (keys %paths) {
my $svnRoot = determineSVNRoot();
my $prefix = chdirReturningRelativePath($svnRoot);
+my $patchSize = 0;
+
# Generate the diffs, in a order chosen for easy reviewing.
for my $path (sort patchpathcmp values %diffFiles) {
- generateDiff($path, $prefix);
+ $patchSize += generateDiff($path, $prefix);
+}
+
+if ($patchSize > 20480) {
+ print STDERR "WARNING: Patch's size is " . int($patchSize/1024) . " kbytes.\n";
+ print STDERR "Patches 20k or smaller are more likely to be reviewed. Larger patches may sit unreviewed for a long time.\n";
}
exit 0;
@@ -130,6 +139,19 @@ sub binarycmp($$)
return $fileDataA->{isBinary} <=> $fileDataB->{isBinary};
}
+sub diffOptionsForFile($)
+{
+ my ($file) = @_;
+
+ my $options = "uaNp";
+
+ if (my $hunkHeaderLineRegEx = hunkHeaderLineRegExForFile($file)) {
+ $options .= "F'$hunkHeaderLineRegEx'";
+ }
+
+ return $options;
+}
+
sub findBaseUrl($)
{
my ($infoPath) = @_;
@@ -196,24 +218,27 @@ sub generateDiff($$)
my $file = File::Spec->catdir($prefix, $fileData->{path});
if ($ignoreChangelogs && basename($file) eq "ChangeLog") {
- return;
+ return 0;
}
- my $patch;
+ my $patch = "";
if ($fileData->{modificationType} eq "additionWithHistory") {
manufacturePatchForAdditionWithHistory($fileData);
}
- open DIFF, "svn diff --diff-cmd diff -x -uaNp '$file' |" or die;
+
+ my $diffOptions = diffOptionsForFile($file);
+ open DIFF, "svn diff --diff-cmd diff -x -$diffOptions '$file' |" or die;
while (<DIFF>) {
$patch .= $_;
}
close DIFF;
$patch = fixChangeLogPatch($patch) if basename($file) eq "ChangeLog";
- print $patch if $patch;
+ print $patch;
if ($fileData->{isBinary}) {
print "\n" if ($patch && $patch =~ m/\n\S+$/m);
outputBinaryContent($file);
}
+ return length($patch);
}
sub generateFileList($\%)
@@ -252,6 +277,15 @@ sub generateFileList($\%)
close STAT;
}
+sub hunkHeaderLineRegExForFile($)
+{
+ my ($file) = @_;
+
+ my $startOfObjCInterfaceRegEx = "@(implementation\\|interface\\|protocol)";
+ return "^[-+]\\|$startOfObjCInterfaceRegEx" if $file =~ /\.mm?$/;
+ return "^$startOfObjCInterfaceRegEx" if $file =~ /^(.*\/)?(mac|objc)\// && $file =~ /\.h$/;
+}
+
sub isBinaryMimeType($)
{
my ($file) = @_;
diff --git a/WebKitTools/Scripts/test-webkitpy b/WebKitTools/Scripts/test-webkitpy
index 8617330..e35c6e6 100755
--- a/WebKitTools/Scripts/test-webkitpy
+++ b/WebKitTools/Scripts/test-webkitpy
@@ -1,5 +1,6 @@
#!/usr/bin/env python
# Copyright (c) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -27,41 +28,213 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import logging
+import os
import sys
-import unittest
-
-from webkitpy.bugzilla_unittest import *
-from webkitpy.buildbot_unittest import *
-from webkitpy.changelogs_unittest import *
-from webkitpy.commands.download_unittest import *
-from webkitpy.commands.early_warning_system_unittest import *
-from webkitpy.commands.openbugs_unittest import OpenBugsTest
-from webkitpy.commands.upload_unittest import *
-from webkitpy.commands.queries_unittest import *
-from webkitpy.commands.queues_unittest import *
-from webkitpy.committers_unittest import *
-from webkitpy.credentials_unittest import *
-from webkitpy.diff_parser_unittest import *
-from webkitpy.executive_unittest import *
-from webkitpy.grammar_unittest import *
-from webkitpy.layout_tests.port.mac_unittest import *
-from webkitpy.multicommandtool_unittest import *
-from webkitpy.networktransaction_unittest import *
-from webkitpy.patchcollection_unittest import *
-from webkitpy.queueengine_unittest import *
-from webkitpy.steps.steps_unittest import *
-from webkitpy.steps.closebugforlanddiff_unittest import *
-from webkitpy.steps.updatechangelogswithreview_unittests import *
-from webkitpy.style.unittests import * # for check-webkit-style
-from webkitpy.user_unittest import *
-from webkitpy.webkit_logging_unittest import *
-from webkitpy.webkitport_unittest import *
+
+# Do not import anything from webkitpy prior to cleaning webkitpy of
+# orphaned *.pyc files. This ensures that no orphaned *.pyc files are
+# accidentally imported during the course of this script.
+#
+# Also, do not import or execute any Python code incompatible with
+# Python 2.4 until after execution of the init() method below.
+
+
+_log = logging.getLogger("test-webkitpy")
+
+
+# Verbose logging is useful for debugging test-webkitpy code that runs
+# before the actual unit tests -- things like autoinstall downloading and
+# unit-test auto-detection logic. This is different from verbose logging
+# of the unit tests themselves (i.e. the unittest module's --verbose flag).
+def configure_logging(is_verbose_logging):
+ """Configure the root logger.
+
+ Configure the root logger not to log any messages from webkitpy --
+ except for messages from the autoinstall module. Also set the
+ logging level as described below.
+
+ Args:
+ is_verbose_logging: A boolean value of whether logging should be
+ verbose. If this parameter is true, the logging
+ level for the handler on the root logger is set to
+ logging.DEBUG. Otherwise, it is set to logging.INFO.
+
+ """
+ # Don't use the Python ternary operator here so that this method will
+ # work with Python 2.4.
+ if is_verbose_logging:
+ logging_level = logging.DEBUG
+ else:
+ logging_level = logging.INFO
+
+ handler = logging.StreamHandler(sys.stderr)
+ # We constrain the level on the handler rather than on the root
+ # logger itself. This is probably better because the handler is
+ # configured and known only to this module, whereas the root logger
+ # is an object shared (and potentially modified) by many modules.
+ # Modifying the handler, then, is less intrusive and less likely to
+ # interfere with modifications made by other modules (e.g. in unit
+ # tests).
+ handler.setLevel(logging_level)
+ formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s")
+ handler.setFormatter(formatter)
+
+ logger = logging.getLogger()
+ logger.addHandler(handler)
+ logger.setLevel(logging.NOTSET)
+
+ # Filter out most webkitpy messages.
+ #
+ # Messages can be selectively re-enabled for this script by updating
+ # this method accordingly.
+ def filter(record):
+ """Filter out autoinstall and non-third-party webkitpy messages."""
+ # FIXME: Figure out a way not to use strings here, for example by
+ # using syntax like webkitpy.test.__name__. We want to be
+ # sure not to import any non-Python 2.4 code, though, until
+ # after the version-checking code has executed.
+ if (record.name.startswith("webkitpy.common.system.autoinstall") or
+ record.name.startswith("webkitpy.test")):
+ return True
+ if record.name.startswith("webkitpy"):
+ return False
+ return True
+
+ testing_filter = logging.Filter()
+ testing_filter.filter = filter
+
+ # Display a message so developers are not mystified as to why
+ # logging does not work in the unit tests.
+ _log.info("Suppressing most webkitpy logging while running unit tests.")
+ handler.addFilter(testing_filter)
+
+
+def _clean_pyc_files(dir_to_clean, paths_not_to_log):
+ """Delete from a directory all .pyc files that have no .py file.
+
+ Args:
+ dir_to_clean: The path to the directory to clean.
+ paths_not_to_log: A list of paths to .pyc files whose deletions should
+ not be logged. This list should normally include
+ only test .pyc files.
+
+ """
+ _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean)
+
+ # Normalize paths not to log.
+ paths_not_to_log = [os.path.abspath(path) for path in paths_not_to_log]
+
+ for dir_path, dir_names, file_names in os.walk(dir_to_clean):
+ for file_name in file_names:
+ if file_name.endswith(".pyc") and file_name[:-1] not in file_names:
+ file_path = os.path.join(dir_path, file_name)
+ if os.path.abspath(file_path) not in paths_not_to_log:
+ _log.info("Deleting orphan *.pyc file: %s" % file_path)
+ os.remove(file_path)
+
+
+# As a substitute for a unit test, this method tests _clean_pyc_files()
+# in addition to calling it. We chose not to use the unittest module
+# because _clean_pyc_files() is called only once and is not used elsewhere.
+def _clean_webkitpy_with_test():
+ webkitpy_dir = os.path.join(os.path.dirname(__file__), "webkitpy")
+
+ # The test .pyc file is--
+ # webkitpy/python24/TEMP_test-webkitpy_test_pyc_file.pyc.
+ test_path = os.path.join(webkitpy_dir, "python24",
+ "TEMP_test-webkitpy_test_pyc_file.pyc")
+
+ test_file = open(test_path, "w")
+ try:
+ test_file.write("Test .pyc file generated by test-webkitpy.")
+ finally:
+ test_file.close()
+
+ # Confirm that the test file exists so that when we check that it does
+ # not exist, the result is meaningful.
+ if not os.path.exists(test_path):
+ raise Exception("Test .pyc file not created: %s" % test_path)
+
+ _clean_pyc_files(webkitpy_dir, [test_path])
+
+ if os.path.exists(test_path):
+ raise Exception("Test .pyc file not deleted: %s" % test_path)
+
+
+def init(command_args):
+ """Execute code prior to importing from webkitpy.unittests.
+
+ Args:
+ command_args: The list of command-line arguments -- usually
+ sys.argv[1:].
+
+ """
+ verbose_logging_flag = "--verbose-logging"
+ is_verbose_logging = verbose_logging_flag in command_args
+ if is_verbose_logging:
+ # Remove the flag so it doesn't cause unittest.main() to error out.
+ #
+ # FIXME: Get documentation for the --verbose-logging flag to show
+ # up in the usage instructions, which are currently generated
+ # by unittest.main(). It's possible that this will require
+ # re-implementing the option parser for unittest.main()
+ # since there may not be an easy way to modify its existing
+ # option parser.
+ sys.argv.remove(verbose_logging_flag)
+
+ configure_logging(is_verbose_logging)
+ _log.debug("Verbose WebKit logging enabled.")
+
+ # We clean orphaned *.pyc files from webkitpy prior to importing from
+ # webkitpy to make sure that no import statements falsely succeed.
+ # This helps to check that import statements have been updated correctly
+ # after any file moves. Otherwise, incorrect import statements can
+ # be masked.
+ #
+ # For example, if webkitpy/python24/versioning.py were moved to a
+ # different location without changing any import statements, and if
+ # the corresponding .pyc file were left behind without deleting it,
+ # then "import webkitpy.python24.versioning" would continue to succeed
+ # even though it would fail for someone checking out a fresh copy
+ # of the source tree. This is because of a Python feature:
+ #
+ # "It is possible to have a file called spam.pyc (or spam.pyo when -O
+ # is used) without a file spam.py for the same module. This can be used
+ # to distribute a library of Python code in a form that is moderately
+ # hard to reverse engineer."
+ #
+ # ( http://docs.python.org/tutorial/modules.html#compiled-python-files )
+ #
+ # Deleting the orphaned .pyc file prior to importing, however, would
+ # cause an ImportError to occur on import as desired.
+ _clean_webkitpy_with_test()
+
+ import webkitpy.python24.versioning as versioning
+
+ versioning.check_version(log=_log)
+
+ (comparison, current_version, minimum_version) = \
+ versioning.compare_version()
+
+ if comparison > 0:
+ # Then the current version is later than the minimum version.
+ message = ("You are testing webkitpy with a Python version (%s) "
+ "higher than the minimum version (%s) it was meant "
+ "to support." % (current_version, minimum_version))
+ _log.warn(message)
+
if __name__ == "__main__":
- # FIXME: This is a hack, but I'm tired of commenting out the test.
- # See https://bugs.webkit.org/show_bug.cgi?id=31818
- if len(sys.argv) > 1 and sys.argv[1] == "--all":
- sys.argv.remove("--all")
- from webkitpy.scm_unittest import *
- unittest.main()
+ init(sys.argv[1:])
+
+ # We import the unit test code after init() to ensure that any
+ # Python version warnings are displayed in case an error occurs
+ # while interpreting webkitpy.unittests. This also allows
+ # logging to be configured prior to importing -- for example to
+ # enable the display of autoinstall logging.log messages while
+ # running the unit tests.
+ from webkitpy.test.main import Tester
+
+ Tester().run_tests(sys.argv)
diff --git a/WebKitTools/Scripts/update-iexploder-cssproperties b/WebKitTools/Scripts/update-iexploder-cssproperties
index b7ae6cb..3fbcf83 100755
--- a/WebKitTools/Scripts/update-iexploder-cssproperties
+++ b/WebKitTools/Scripts/update-iexploder-cssproperties
@@ -1,6 +1,7 @@
#!/usr/bin/perl
# Copyright (C) 2007 Apple Inc. All rights reserved.
+# Copyright (C) 2010 Holger Hans Peter Freyther
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -26,87 +27,103 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# This script updates WebKitTools/iExploder/htdocs/cssproperties.in based on
-# WebCore/css/CSSPropertyNames.in.
+# This script updates WebKitTools/iExploder/htdocs/*.in based on
+# WebCore/css/CSSPropertyNames.in, WebCore/html/HTMLTagNames.in
+# and WebCore/html/HTMLAttributeNames.in
use warnings;
use strict;
use FindBin;
use lib $FindBin::Bin;
+use VCSUtils;
use webkitdirs;
use File::Spec;
-sub generateSectionFromCSSPropertyNamesFile();
-sub readiExploderFile();
-sub svnRevision($);
-sub writeiExploderFile();
+sub generateEntityListFromFile($);
+sub readiExploderFile($);
+sub update($$);
+sub writeiExploderFile($@);
-my $iExploderFile = File::Spec->catfile(sourceDir(), split("/", "WebKitTools/iExploder/htdocs/cssproperties.in"));
-my $cssPropertyNamesFile = File::Spec->catfile(sourceDir(), split("/", "WebCore/css/CSSPropertyNames.in"));
-
-my @sections = readiExploderFile();
-$sections[0] = generateSectionFromCSSPropertyNamesFile();
-writeiExploderFile();
-
-print `svn stat $iExploderFile`;
+update("cssproperties.in", "css/CSSPropertyNames.in");
+update("htmlattrs.in", "html/HTMLAttributeNames.in");
+update("htmltags.in", "html/HTMLTagNames.in");
print "Successfully updated!\n";
exit 0;
-sub generateSectionFromCSSPropertyNamesFile()
+sub generateEntityListFromFile($)
{
- my $revision = svnRevision($cssPropertyNamesFile);
- my $path = File::Spec->abs2rel($cssPropertyNamesFile, sourceDir());
+ my ($filename) = @_;
+
+ my $revision = svnRevisionForDirectory(dirname($filename));
+ my $path = File::Spec->abs2rel($filename, sourceDir());
my $result = "# From WebKit svn r" . $revision . " (" . $path . ")\n";
- my @properties = ();
+ my @entities = ();
+ my $in_namespace = 0;
- open(IN, $cssPropertyNamesFile) || die "$!";
+ open(IN, $filename) || die "$!";
while (my $l = <IN>) {
chomp $l;
+ if ($l =~ m/^namespace=\"/) {
+ $in_namespace = 1;
+ } elsif ($in_namespace && $l =~ m/^$/) {
+ $in_namespace = 0;
+ }
+
+ next if $in_namespace;
next if $l =~ m/^\s*#/ || $l =~ m/^\s*$/;
- push(@properties, $l);
+
+ # For HTML Tags that can have additional information
+ if ($l =~ m/ /) {
+ my @split = split / /, $l;
+ $l = $split[0]
+ }
+
+ push(@entities, $l);
}
close(IN);
- $result .= join("\n", sort { $a cmp $b } @properties) . "\n\n";
+ $result .= join("\n", sort { $a cmp $b } @entities) . "\n\n";
return $result;
}
-sub readiExploderFile()
+sub readiExploderFile($)
{
+ my ($filename) = @_;
+
my @sections = ();
local $/ = "\n\n";
- open(IN, $iExploderFile) || die "$!";
+ open(IN, $filename) || die "$!";
@sections = <IN>;
close(IN);
return @sections;
}
-sub svnRevision($)
+sub update($$)
{
- my ($file) = @_;
- my $revision = "";
+ my ($iexploderPath, $webcorePath) = @_;
- open INFO, "svn info '$file' |" or die;
- while (<INFO>) {
- if (/^Revision: (.+)/) {
- $revision = $1;
- }
- }
- close INFO;
+ $iexploderPath = File::Spec->catfile(sourceDir(), "WebKitTools", "iExploder", "htdocs", split("/", $iexploderPath));
+ $webcorePath = File::Spec->catfile(sourceDir(), "WebCore", split("/", $webcorePath));
- return $revision ? $revision : "UNKNOWN";
+ my @sections = readiExploderFile($iexploderPath);
+ $sections[0] = generateEntityListFromFile($webcorePath);
+ writeiExploderFile($iexploderPath, @sections);
}
-sub writeiExploderFile()
+
+sub writeiExploderFile($@)
{
- open(OUT, "> $iExploderFile") || die "$!";
+ my ($filename, @sections) = @_;
+
+ open(OUT, "> $filename") || die "$!";
print OUT join("", @sections);
close(OUT);
}
+
diff --git a/WebKitTools/Scripts/validate-committer-lists b/WebKitTools/Scripts/validate-committer-lists
index 2f2dd32..ad3d358 100755
--- a/WebKitTools/Scripts/validate-committer-lists
+++ b/WebKitTools/Scripts/validate-committer-lists
@@ -36,13 +36,13 @@ import subprocess
import re
import urllib2
from datetime import date, datetime, timedelta
-from webkitpy.committers import CommitterList
-from webkitpy.webkit_logging import log, error
+from webkitpy.common.config.committers import CommitterList
+from webkitpy.common.system.deprecated_logging import log, error
from webkitpy.scm import Git
# WebKit includes a built copy of BeautifulSoup in Scripts/webkitpy
# so this import should always succeed.
-from webkitpy.BeautifulSoup import BeautifulSoup
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
def print_list_if_non_empty(title, list_to_print):
if not list_to_print:
diff --git a/WebKitTools/Scripts/webkit-patch b/WebKitTools/Scripts/webkit-patch
index b4bcc4c..e0170ed 100755
--- a/WebKitTools/Scripts/webkit-patch
+++ b/WebKitTools/Scripts/webkit-patch
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# Copyright (c) 2009, Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -31,79 +32,25 @@
# A tool for automating dealing with bugzilla, posting patches, committing patches, etc.
import os
+import sys
-from webkitpy.bugzilla import Bugzilla
-from webkitpy.buildbot import BuildBot
-from webkitpy.commands.download import *
-from webkitpy.commands.early_warning_system import *
-from webkitpy.commands.openbugs import OpenBugs
-from webkitpy.commands.queries import *
-from webkitpy.commands.queues import *
-from webkitpy.commands.upload import *
-from webkitpy.executive import Executive
-from webkitpy.webkit_logging import log
-from webkitpy.multicommandtool import MultiCommandTool
-from webkitpy.scm import detect_scm_system
-from webkitpy.user import User
+from webkitpy.common.system.logutils import configure_logging
+import webkitpy.python24.versioning as versioning
-class WebKitPatch(MultiCommandTool):
- global_options = [
- make_option("--dry-run", action="store_true", dest="dry_run", default=False, help="do not touch remote servers"),
- make_option("--status-host", action="store", dest="status_host", type="string", nargs=1, help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted."),
- ]
+def main():
+ configure_logging()
- def __init__(self):
- MultiCommandTool.__init__(self)
+ versioning.check_version()
- self.bugs = Bugzilla()
- self.buildbot = BuildBot()
- self.executive = Executive()
- self.user = User()
- self._scm = None
- self.status_server = StatusServer()
+ # Import webkit-patch code only after version-checking so that
+ # script doesn't error out before having a chance to report the
+ # version warning.
+ from webkitpy.tool.main import WebKitPatch
- def scm(self):
- # Lazily initialize SCM to not error-out before command line parsing (or when running non-scm commands).
- original_cwd = os.path.abspath(".")
- if not self._scm:
- self._scm = detect_scm_system(original_cwd)
-
- if not self._scm:
- script_directory = os.path.abspath(sys.path[0])
- webkit_directory = os.path.abspath(os.path.join(script_directory, "../.."))
- self._scm = detect_scm_system(webkit_directory)
- if self._scm:
- log("The current directory (%s) is not a WebKit checkout, using %s" % (original_cwd, webkit_directory))
- else:
- error("FATAL: Failed to determine the SCM system for either %s or %s" % (original_cwd, webkit_directory))
-
- return self._scm
-
- def path(self):
- return __file__
-
- def should_show_in_main_help(self, command):
- if not command.show_in_main_help:
- return False
- if command.requires_local_commits:
- return self.scm().supports_local_commits()
- return True
-
- # FIXME: This may be unnecessary since we pass global options to all commands during execute() as well.
- def handle_global_options(self, options):
- if options.dry_run:
- self.scm().dryrun = True
- self.bugs.dryrun = True
- if options.status_host:
- self.status_server.set_host(options.status_host)
-
- def should_execute_command(self, command):
- if command.requires_local_commits and not self.scm().supports_local_commits():
- failure_reason = "%s requires local commits using %s in %s." % (command.name, self.scm().display_name(), self.scm().checkout_root)
- return (False, failure_reason)
- return (True, None)
+ WebKitPatch(__file__).main()
if __name__ == "__main__":
- WebKitPatch().main()
+
+ main()
diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm
index 7985790..0b18373 100644
--- a/WebKitTools/Scripts/webkitdirs.pm
+++ b/WebKitTools/Scripts/webkitdirs.pm
@@ -63,6 +63,7 @@ my $isSymbian;
my %qtFeatureDefaults;
my $isGtk;
my $isWx;
+my $isEfl;
my @wxArgs;
my $isChromium;
my $isInspectorFrontend;
@@ -71,6 +72,7 @@ my $isInspectorFrontend;
my $vcBuildPath;
my $windowsTmpPath;
my $windowsSourceDir;
+my $willUseVCExpressWhenBuilding = 0;
# Defined in VCSUtils.
sub exitStatus($);
@@ -245,6 +247,7 @@ sub argumentsForConfiguration()
push(@args, '--qt') if isQt();
push(@args, '--symbian') if isSymbian();
push(@args, '--gtk') if isGtk();
+ push(@args, '--efl') if isEfl();
push(@args, '--wx') if isWx();
push(@args, '--chromium') if isChromium();
push(@args, '--inspector-frontend') if isInspectorFrontend();
@@ -270,11 +273,11 @@ sub determineConfigurationProductDir
if (isAppleWinWebKit() && !isWx()) {
$configurationProductDir = "$baseProductDir/bin";
} else {
- # [Gtk] We don't have Release/Debug configurations in straight
+ # [Gtk][Efl] We don't have Release/Debug configurations in straight
# autotool builds (non build-webkit). In this case and if
# WEBKITOUTPUTDIR exist, use that as our configuration dir. This will
# allows us to run run-webkit-tests without using build-webkit.
- if ($ENV{"WEBKITOUTPUTDIR"} && isGtk()) {
+ if ($ENV{"WEBKITOUTPUTDIR"} && (isGtk() || isEfl())) {
$configurationProductDir = "$baseProductDir";
} else {
$configurationProductDir = "$baseProductDir/$configuration";
@@ -325,7 +328,7 @@ sub jscProductDir
my $productDir = productDir();
$productDir .= "/JavaScriptCore" if isQt();
$productDir .= "/$configuration" if (isQt() && isWindows());
- $productDir .= "/Programs" if isGtk();
+ $productDir .= "/Programs" if (isGtk() || isEfl());
return $productDir;
}
@@ -541,6 +544,11 @@ sub builtDylibPathForName
if (isDarwin() and -d "$configurationProductDir/lib/$libraryName.framework") {
return "$configurationProductDir/lib/$libraryName.framework/$libraryName";
} elsif (isWindows()) {
+ if (configuration() eq "Debug") {
+ # On Windows, there is a "d" suffix to the library name. See <http://trac.webkit.org/changeset/53924/>.
+ $libraryName .= "d";
+ }
+
my $mkspec = `qmake -query QMAKE_MKSPECS`;
$mkspec =~ s/[\n|\r]$//g;
my $qtMajorVersion = retrieveQMakespecVar("$mkspec/qconfig.pri", "QT_MAJOR_VERSION");
@@ -558,6 +566,9 @@ sub builtDylibPathForName
if (isGtk()) {
return "$configurationProductDir/$libraryName/../.libs/libwebkit-1.0.so";
}
+ if (isEfl()) {
+ return "$configurationProductDir/$libraryName/../.libs/libewebkit.so";
+ }
if (isAppleMacWebKit()) {
return "$configurationProductDir/$libraryName.framework/Versions/A/$libraryName";
}
@@ -569,7 +580,7 @@ sub builtDylibPathForName
}
}
- die "Unsupported platform, can't determine built library locations.";
+ die "Unsupported platform, can't determine built library locations.\nTry `build-webkit --help` for more information.\n";
}
# Check to see that all the frameworks are built.
@@ -657,8 +668,8 @@ sub determineIsQt()
return;
}
- # The presence of QTDIR only means Qt if --gtk is not on the command-line
- if (isGtk() || isWx()) {
+ # The presence of QTDIR only means Qt if --gtk or --wx or --efl are not on the command-line
+ if (isGtk() || isWx() || isEfl()) {
$isQt = 0;
return;
}
@@ -678,6 +689,18 @@ sub determineIsSymbian()
$isSymbian = defined($ENV{'EPOCROOT'});
}
+sub determineIsEfl()
+{
+ return if defined($isEfl);
+ $isEfl = checkForArgumentAndRemoveFromARGV("--efl");
+}
+
+sub isEfl()
+{
+ determineIsEfl();
+ return $isEfl;
+}
+
sub isGtk()
{
determineIsGtk();
@@ -769,7 +792,7 @@ sub isLinux()
sub isAppleWebKit()
{
- return !(isQt() or isGtk() or isWx() or isChromium());
+ return !(isQt() or isGtk() or isWx() or isChromium() or isEfl());
}
sub isAppleMacWebKit()
@@ -856,7 +879,7 @@ sub relativeScriptsDir()
sub launcherPath()
{
my $relativeScriptsPath = relativeScriptsDir();
- if (isGtk() || isQt() || isWx()) {
+ if (isGtk() || isQt() || isWx() || isEfl()) {
return "$relativeScriptsPath/run-launcher";
} elsif (isAppleWebKit()) {
return "$relativeScriptsPath/run-safari";
@@ -873,6 +896,8 @@ sub launcherName()
return "wxBrowser";
} elsif (isAppleWebKit()) {
return "Safari";
+ } elsif (isEfl()) {
+ return "EWebLauncher";
}
}
@@ -895,7 +920,7 @@ sub checkRequiredSystemConfig
print "http://developer.apple.com/tools/xcode\n";
print "*************************************************************\n";
}
- } elsif (isGtk() or isQt() or isWx()) {
+ } elsif (isGtk() or isQt() or isWx() or isEfl()) {
my @cmds = qw(flex bison gperf);
my @missing = ();
foreach my $cmd (@cmds) {
@@ -1002,6 +1027,7 @@ sub setupCygwinEnv()
print "*************************************************************\n";
die;
}
+ $willUseVCExpressWhenBuilding = 1;
}
my $qtSDKPath = "$programFilesPath/QuickTime SDK";
@@ -1023,6 +1049,23 @@ sub setupCygwinEnv()
print "WEBKITLIBRARIESDIR is set to: ", $ENV{"WEBKITLIBRARIESDIR"}, "\n";
}
+sub dieIfWindowsPlatformSDKNotInstalled
+{
+ my $windowsPlatformSDKRegistryEntry = "/proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MicrosoftSDK/InstalledSDKs/D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1";
+
+ return if -e $windowsPlatformSDKRegistryEntry;
+
+ print "*************************************************************\n";
+ print "Cannot find '$windowsPlatformSDKRegistryEntry'.\n";
+ print "Please download and install the Microsoft Windows Server 2003 R2\n";
+ print "Platform SDK from <http://www.microsoft.com/downloads/details.aspx?\n";
+ print "familyid=0baf2b35-c656-4969-ace8-e4c0c0716adb&displaylang=en>.\n\n";
+ print "Then follow step 2 in the Windows section of the \"Installing Developer\n";
+ print "Tools\" instructions at <http://www.webkit.org/building/tools.html>.\n";
+ print "*************************************************************\n";
+ die;
+}
+
sub copyInspectorFrontendFiles
{
my $productDir = productDir();
@@ -1040,6 +1083,9 @@ sub copyInspectorFrontendFiles
} elsif (isQt() || isGtk()) {
my $prefix = $ENV{"WebKitInstallationPrefix"};
$inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/webkit-1.0/webinspector";
+ } elsif (isEfl()) {
+ my $prefix = $ENV{"WebKitInstallationPrefix"};
+ $inspectorResourcesDirPath = (defined($prefix) ? $prefix : "/usr/share") . "/ewebkit/webinspector";
}
if (! -d $inspectorResourcesDirPath) {
@@ -1074,6 +1120,8 @@ sub buildVisualStudioProject
my $config = configurationForVisualStudio();
+ dieIfWindowsPlatformSDKNotInstalled() if $willUseVCExpressWhenBuilding;
+
chomp(my $winProjectPath = `cygpath -w "$project"`);
my $action = "/build";
@@ -1403,6 +1451,19 @@ sub buildChromiumVisualStudioProject($$)
$vsInstallDir = `cygpath "$vsInstallDir"` if isCygwin();
chomp $vsInstallDir;
$vcBuildPath = "$vsInstallDir/Common7/IDE/devenv.com";
+ if (! -e $vcBuildPath) {
+ # Visual Studio not found, try VC++ Express
+ $vcBuildPath = "$vsInstallDir/Common7/IDE/VCExpress.exe";
+ if (! -e $vcBuildPath) {
+ print "*************************************************************\n";
+ print "Cannot find '$vcBuildPath'\n";
+ print "Please execute the file 'vcvars32.bat' from\n";
+ print "'$programFilesPath\\Microsoft Visual Studio 8\\VC\\bin\\'\n";
+ print "to setup the necessary environment variables.\n";
+ print "*************************************************************\n";
+ die;
+ }
+ }
# Create command line and execute it.
my @command = ($vcBuildPath, $projectPath, $action, $config);
@@ -1491,4 +1552,44 @@ sub runSafari
return 1;
}
+sub runMiniBrowser
+{
+ if (isAppleMacWebKit()) {
+ my $productDir = productDir();
+ print "Starting MiniBrowser with DYLD_FRAMEWORK_PATH set to point to $productDir.\n";
+ $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
+ $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = "YES";
+ my $miniBrowserPath = "$productDir/MiniBrowser.app/Contents/MacOS/MiniBrowser";
+ if (!isTiger() && architecture()) {
+ return system "arch", "-" . architecture(), $miniBrowserPath, @ARGV;
+ } else {
+ return system $miniBrowserPath, @ARGV;
+ }
+ }
+
+ return 1;
+}
+
+sub debugMiniBrowser
+{
+ if (isAppleMacWebKit()) {
+ my $gdbPath = "/usr/bin/gdb";
+ die "Can't find gdb executable. Is gdb installed?\n" unless -x $gdbPath;
+
+ my $productDir = productDir();
+
+ $ENV{DYLD_FRAMEWORK_PATH} = $productDir;
+ $ENV{WEBKIT_UNSET_DYLD_FRAMEWORK_PATH} = 'YES';
+
+ my $miniBrowserPath = "$productDir/MiniBrowser.app/Contents/MacOS/MiniBrowser";
+
+ print "Starting MiniBrowser under gdb with DYLD_FRAMEWORK_PATH set to point to built WebKit2 in $productDir.\n";
+ my @architectureFlags = ("-arch", architecture()) if !isTiger();
+ exec $gdbPath, @architectureFlags, $miniBrowserPath or die;
+ return;
+ }
+
+ return 1;
+}
+
1;
diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl
new file mode 100644
index 0000000..a226e43
--- /dev/null
+++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/mergeChangeLogs.pl
@@ -0,0 +1,336 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2010 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 INC. 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 INC. 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.
+
+# Unit tests of VCSUtils::mergeChangeLogs().
+
+use strict;
+
+use Test::Simple tests => 16;
+use File::Temp qw(tempfile);
+use VCSUtils;
+
+# Read contents of a file and return it.
+sub readFile($)
+{
+ my ($fileName) = @_;
+
+ local $/;
+ open(FH, "<", $fileName);
+ my $content = <FH>;
+ close(FH);
+
+ return $content;
+}
+
+# Write a temporary file and return the filename.
+sub writeTempFile($$$)
+{
+ my ($name, $extension, $content) = @_;
+
+ my ($FH, $fileName) = tempfile(
+ $name . "-XXXXXXXX",
+ DIR => ($ENV{'TMPDIR'} || "/tmp"),
+ UNLINK => 0,
+ );
+ print $FH $content;
+ close $FH;
+
+ if ($extension) {
+ my $newFileName = $fileName . $extension;
+ rename($fileName, $newFileName);
+ $fileName = $newFileName;
+ }
+
+ return $fileName;
+}
+
+# --------------------------------------------------------------------------------
+
+{
+ # New test
+ my $title = "mergeChangeLogs: traditional rejected patch success";
+
+ my $fileNewerContent = <<'EOF';
+2010-01-29 Mark Rowe <mrowe@apple.com>
+
+ Fix the Mac build.
+
+ Disable ENABLE_INDEXED_DATABASE since it is "completely non-functional".
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileNewer = writeTempFile("file", "", $fileNewerContent);
+
+ my $fileMineContent = <<'EOF';
+***************
+*** 1,3 ****
+ 2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+--- 1,9 ----
++ 2010-01-29 Oliver Hunt <oliver@apple.com>
++
++ Reviewed by Darin Adler.
++
++ JSC is failing to propagate anonymous slot count on some transitions
++
+ 2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+EOF
+ my $fileMine = writeTempFile("file", ".rej", $fileMineContent);
+ rename($fileMine, $fileNewer . ".rej");
+ $fileMine = $fileNewer . ".rej";
+
+ my $fileOlderContent = $fileNewerContent;
+ my $fileOlder = writeTempFile("file", ".orig", $fileOlderContent);
+ rename($fileOlder, $fileNewer . ".orig");
+ $fileOlder = $fileNewer . ".orig";
+
+ my $exitStatus = mergeChangeLogs($fileMine, $fileOlder, $fileNewer);
+
+ # mergeChangeLogs() should return 1 since the patch succeeded.
+ ok($exitStatus == 1, "$title: should return 1 for success");
+
+ ok(readFile($fileMine) eq $fileMineContent, "$title: \$fileMine should be unchanged");
+ ok(readFile($fileOlder) eq $fileOlderContent, "$title: \$fileOlder should be unchanged");
+
+ my $expectedContent = <<'EOF';
+2010-01-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Darin Adler.
+
+ JSC is failing to propagate anonymous slot count on some transitions
+
+EOF
+ $expectedContent .= $fileNewerContent;
+ ok(readFile($fileNewer) eq $expectedContent, "$title: \$fileNewer should be updated to include patch");
+
+ unlink($fileMine, $fileOlder, $fileNewer);
+}
+
+# --------------------------------------------------------------------------------
+
+{
+ # New test
+ my $title = "mergeChangeLogs: traditional rejected patch failure";
+
+ my $fileNewerContent = <<'EOF';
+2010-01-29 Mark Rowe <mrowe@apple.com>
+
+ Fix the Mac build.
+
+ Disable ENABLE_INDEXED_DATABASE since it is "completely non-functional".
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileNewer = writeTempFile("file", "", $fileNewerContent);
+
+ my $fileMineContent = <<'EOF';
+***************
+*** 1,9 ****
+- 2010-01-29 Oliver Hunt <oliver@apple.com>
+-
+- Reviewed by Darin Adler.
+-
+- JSC is failing to propagate anonymous slot count on some transitions
+-
+ 2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+--- 1,3 ----
+ 2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+EOF
+ my $fileMine = writeTempFile("file", ".rej", $fileMineContent);
+ rename($fileMine, $fileNewer . ".rej");
+ $fileMine = $fileNewer . ".rej";
+
+ my $fileOlderContent = $fileNewerContent;
+ my $fileOlder = writeTempFile("file", ".orig", $fileOlderContent);
+ rename($fileOlder, $fileNewer . ".orig");
+ $fileOlder = $fileNewer . ".orig";
+
+ my $exitStatus = mergeChangeLogs($fileMine, $fileOlder, $fileNewer);
+
+ # mergeChangeLogs() should return 0 since the patch failed.
+ ok($exitStatus == 0, "$title: should return 0 for failure");
+
+ ok(readFile($fileMine) eq $fileMineContent, "$title: \$fileMine should be unchanged");
+ ok(readFile($fileOlder) eq $fileOlderContent, "$title: \$fileOlder should be unchanged");
+ ok(readFile($fileNewer) eq $fileNewerContent, "$title: \$fileNewer should be unchanged");
+
+ unlink($fileMine, $fileOlder, $fileNewer);
+}
+
+# --------------------------------------------------------------------------------
+
+{
+ # New test
+ my $title = "mergeChangeLogs: patch succeeds";
+
+ my $fileMineContent = <<'EOF';
+2010-01-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Darin Adler.
+
+ JSC is failing to propagate anonymous slot count on some transitions
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileMine = writeTempFile("fileMine", "", $fileMineContent);
+
+ my $fileOlderContent = <<'EOF';
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileOlder = writeTempFile("fileOlder", "", $fileOlderContent);
+
+ my $fileNewerContent = <<'EOF';
+2010-01-29 Mark Rowe <mrowe@apple.com>
+
+ Fix the Mac build.
+
+ Disable ENABLE_INDEXED_DATABASE since it is "completely non-functional".
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileNewer = writeTempFile("fileNewer", "", $fileNewerContent);
+
+ my $exitStatus = mergeChangeLogs($fileMine, $fileOlder, $fileNewer);
+
+ # mergeChangeLogs() should return 1 since the patch succeeded.
+ ok($exitStatus == 1, "$title: should return 1 for success");
+
+ ok(readFile($fileMine) eq $fileMineContent, "$title: \$fileMine should be unchanged");
+ ok(readFile($fileOlder) eq $fileOlderContent, "$title: \$fileOlder should be unchanged");
+
+ my $expectedContent = <<'EOF';
+2010-01-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Darin Adler.
+
+ JSC is failing to propagate anonymous slot count on some transitions
+
+EOF
+ $expectedContent .= $fileNewerContent;
+
+ ok(readFile($fileNewer) eq $expectedContent, "$title: \$fileNewer should be patched");
+
+ unlink($fileMine, $fileOlder, $fileNewer);
+}
+
+# --------------------------------------------------------------------------------
+
+{
+ # New test
+ my $title = "mergeChangeLogs: patch fails";
+
+ my $fileMineContent = <<'EOF';
+2010-01-29 Mark Rowe <mrowe@apple.com>
+
+ Fix the Mac build.
+
+ Disable ENABLE_INDEXED_DATABASE since it is "completely non-functional".
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileMine = writeTempFile("fileMine", "", $fileMineContent);
+
+ my $fileOlderContent = <<'EOF';
+2010-01-29 Mark Rowe <mrowe@apple.com>
+
+ Fix the Mac build.
+
+ Disable ENABLE_INDEXED_DATABASE since it is "completely non-functional".
+
+2010-01-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Darin Adler.
+
+ JSC is failing to propagate anonymous slot count on some transitions
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileOlder = writeTempFile("fileOlder", "", $fileOlderContent);
+
+ my $fileNewerContent = <<'EOF';
+2010-01-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Darin Adler.
+
+ JSC is failing to propagate anonymous slot count on some transitions
+
+2010-01-29 Simon Hausmann <simon.hausmann@nokia.com>
+
+ Rubber-stamped by Maciej Stachowiak.
+
+ Fix the ARM build.
+EOF
+ my $fileNewer = writeTempFile("fileNewer", "", $fileNewerContent);
+
+ my $exitStatus = mergeChangeLogs($fileMine, $fileOlder, $fileNewer);
+
+ # mergeChangeLogs() should return a non-zero exit status since the patch failed.
+ ok($exitStatus == 0, "$title: return non-zero exit status for failure");
+
+ ok(readFile($fileMine) eq $fileMineContent, "$title: \$fileMine should be unchanged");
+ ok(readFile($fileOlder) eq $fileOlderContent, "$title: \$fileOlder should be unchanged");
+
+ # $fileNewer should still exist unchanged because the patch failed
+ ok(readFile($fileNewer) eq $fileNewerContent, "$title: \$fileNewer should be unchanged");
+
+ unlink($fileMine, $fileOlder, $fileNewer);
+}
+
+# --------------------------------------------------------------------------------
+
diff --git a/WebKitTools/Scripts/webkitperl/httpd.pm b/WebKitTools/Scripts/webkitperl/httpd.pm
index 05eb21c..240f368 100644
--- a/WebKitTools/Scripts/webkitperl/httpd.pm
+++ b/WebKitTools/Scripts/webkitperl/httpd.pm
@@ -31,6 +31,7 @@
use strict;
use warnings;
+use File::Copy;
use File::Path;
use File::Spec;
use File::Spec::Functions;
diff --git a/WebKitTools/Scripts/webkitpy/__init__.py b/WebKitTools/Scripts/webkitpy/__init__.py
index 94ecc70..b376bf2 100644
--- a/WebKitTools/Scripts/webkitpy/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/__init__.py
@@ -1,8 +1,13 @@
# Required for Python to search this directory for module files
-import autoinstall
-
-# List our third-party library dependencies here and where they can be
-# downloaded.
-autoinstall.bind("ClientForm", "http://pypi.python.org/packages/source/C/ClientForm/ClientForm-0.2.10.zip", "ClientForm-0.2.10")
-autoinstall.bind("mechanize", "http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip", "mechanize-0.1.11")
+# Keep this file free of any code or import statements that could
+# cause either an error to occur or a log message to be logged.
+# This ensures that calling code can import initialization code from
+# webkitpy before any errors or log messages due to code in this file.
+# Initialization code can include things like version-checking code and
+# logging configuration code.
+#
+# We do not execute any version-checking code or logging configuration
+# code in this file so that callers can opt-in as they want. This also
+# allows different callers to choose different initialization code,
+# as necessary.
diff --git a/WebKitTools/Scripts/webkitpy/autoinstall.py b/WebKitTools/Scripts/webkitpy/autoinstall.py
deleted file mode 100644
index 467e6b4..0000000
--- a/WebKitTools/Scripts/webkitpy/autoinstall.py
+++ /dev/null
@@ -1,335 +0,0 @@
-# Copyright (c) 2009, Daniel Krech 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 the Daniel Krech 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
-# HOLDER 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.
-
-"""\
-package loader for auto installing Python packages.
-
-A package loader in the spirit of Zero Install that can be used to
-inject dependencies into the import process.
-
-
-To install::
-
- easy_install -U autoinstall
-
- or
-
- download, unpack, python setup.py install
-
- or
-
- try the bootstrap loader. See below.
-
-
-To use::
-
- # You can bind any package name to a URL pointing to something
- # that can be imported using the zipimporter.
-
- autoinstall.bind("pymarc", "http://pypi.python.org/packages/2.5/p/pymarc/pymarc-2.1-py2.5.egg")
-
- import pymarc
-
- print pymarc.__version__, pymarc.__file__
-
-
-Changelog::
-
-- added support for non top level packages.
-- cache files now use filename part from URL.
-- applied patch from Eric Seidel <eseidel@google.com> to add support
-for loading modules where the module is not at the root of the .zip
-file.
-
-
-TODO::
-
-- a description of the intended use case
-- address other issues pointed out in:
-
- http://mail.python.org/pipermail/python-dev/2008-March/077926.html
-
-Scribbles::
-
-pull vs. push
-user vs. system
-web vs. filesystem
-auto vs. manual
-
-manage development sandboxes
-
-optional interfaces...
-
- def get_data(pathname) -> string with file data.
-
- Return the data associated with 'pathname'. Raise IOError if
- the file wasn't found.");
-
- def is_package,
- "is_package(fullname) -> bool.
-
- Return True if the module specified by fullname is a package.
- Raise ZipImportError is the module couldn't be found.");
-
- def get_code,
- "get_code(fullname) -> code object.
-
- Return the code object for the specified module. Raise ZipImportError
- is the module couldn't be found.");
-
- def get_source,
- "get_source(fullname) -> source string.
-
- Return the source code for the specified module. Raise ZipImportError
- is the module couldn't be found, return None if the archive does
- contain the module, but has no source for it.");
-
-
-Autoinstall can also be bootstraped with the nascent package loader
-bootstrap module. For example::
-
- # or via the bootstrap
- # loader.
-
- try:
- _version = "0.2"
- import autoinstall
- if autoinstall.__version__ != _version:
- raise ImportError("A different version than expected found.")
- except ImportError, e:
- # http://svn.python.org/projects/sandbox/trunk/bootstrap/bootstrap.py
- import bootstrap
- pypi = "http://pypi.python.org"
- dir = "packages/source/a/autoinstall"
- url = "%s/%s/autoinstall-%s.tar.gz" % (pypi, dir, _version)
- bootstrap.main((url,))
- import autoinstall
-
-References::
-
- http://0install.net/
- http://www.python.org/dev/peps/pep-0302/
- http://svn.python.org/projects/sandbox/trunk/import_in_py
- http://0install.net/injector-find.html
- http://roscidus.com/desktop/node/903
-
-"""
-
-# To allow use of the "with" keyword for Python 2.5 users.
-from __future__ import with_statement
-
-__version__ = "0.2"
-__docformat__ = "restructuredtext en"
-
-import os
-import new
-import sys
-import urllib
-import logging
-import tempfile
-import zipimport
-
-_logger = logging.getLogger(__name__)
-
-
-_importer = None
-
-def _getImporter():
- global _importer
- if _importer is None:
- _importer = Importer()
- sys.meta_path.append(_importer)
- return _importer
-
-def bind(package_name, url, zip_subpath=None):
- """bind a top level package name to a URL.
-
- The package name should be a package name and the url should be a
- url to something that can be imported using the zipimporter.
-
- Optional zip_subpath parameter allows searching for modules
- below the root level of the zip file.
- """
- _getImporter().bind(package_name, url, zip_subpath)
-
-
-class Cache(object):
-
- def __init__(self, directory=None):
- if directory is None:
- # Default to putting the cache directory in the same directory
- # as this file.
- containing_directory = os.path.dirname(__file__)
- directory = os.path.join(containing_directory, "autoinstall.cache.d");
-
- self.directory = directory
- try:
- if not os.path.exists(self.directory):
- self._create_cache_directory()
- except Exception, err:
- _logger.exception(err)
- self.cache_directry = tempfile.mkdtemp()
- _logger.info("Using cache directory '%s'." % self.directory)
-
- def _create_cache_directory(self):
- _logger.debug("Creating cache directory '%s'." % self.directory)
- os.mkdir(self.directory)
- readme_path = os.path.join(self.directory, "README")
- with open(readme_path, "w") as f:
- f.write("This directory was auto-generated by '%s'.\n"
- "It is safe to delete.\n" % __file__)
-
- def get(self, url):
- _logger.info("Getting '%s' from cache." % url)
- filename = url.rsplit("/")[-1]
-
- # so that source url is significant in determining cache hits
- d = os.path.join(self.directory, "%s" % hash(url))
- if not os.path.exists(d):
- os.mkdir(d)
-
- filename = os.path.join(d, filename)
-
- if os.path.exists(filename):
- _logger.debug("... already cached in file '%s'." % filename)
- else:
- _logger.debug("... not in cache. Caching in '%s'." % filename)
- stream = file(filename, "wb")
- self.download(url, stream)
- stream.close()
- return filename
-
- def download(self, url, stream):
- _logger.info("Downloading: %s" % url)
- try:
- netstream = urllib.urlopen(url)
- code = 200
- if hasattr(netstream, "getcode"):
- code = netstream.getcode()
- if not 200 <= code < 300:
- raise ValueError("HTTP Error code %s" % code)
- except Exception, err:
- _logger.exception(err)
-
- BUFSIZE = 2**13 # 8KB
- size = 0
- while True:
- data = netstream.read(BUFSIZE)
- if not data:
- break
- stream.write(data)
- size += len(data)
- netstream.close()
- _logger.info("Downloaded %d bytes." % size)
-
-
-class Importer(object):
-
- def __init__(self):
- self.packages = {}
- self.__cache = None
-
- def __get_store(self):
- return self.__store
- store = property(__get_store)
-
- def _get_cache(self):
- if self.__cache is None:
- self.__cache = Cache()
- return self.__cache
- def _set_cache(self, cache):
- self.__cache = cache
- cache = property(_get_cache, _set_cache)
-
- def find_module(self, fullname, path=None):
- """-> self or None.
-
- Search for a module specified by 'fullname'. 'fullname' must be
- the fully qualified (dotted) module name. It returns the
- zipimporter instance itself if the module was found, or None if
- it wasn't. The optional 'path' argument is ignored -- it's
- there for compatibility with the importer protocol.");
- """
- _logger.debug("find_module(%s, path=%s)" % (fullname, path))
-
- if fullname in self.packages:
- (url, zip_subpath) = self.packages[fullname]
- filename = self.cache.get(url)
- zip_path = "%s/%s" % (filename, zip_subpath) if zip_subpath else filename
- _logger.debug("fullname: %s url: %s path: %s zip_path: %s" % (fullname, url, path, zip_path))
- try:
- loader = zipimport.zipimporter(zip_path)
- _logger.debug("returning: %s" % loader)
- except Exception, e:
- _logger.exception(e)
- return None
- return loader
- return None
-
- def bind(self, package_name, url, zip_subpath):
- _logger.info("binding: %s -> %s subpath: %s" % (package_name, url, zip_subpath))
- self.packages[package_name] = (url, zip_subpath)
-
-
-if __name__=="__main__":
- import logging
- #logging.basicConfig()
- logger = logging.getLogger()
-
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
- # set a format which is simpler for console use
- formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
- # tell the handler to use this format
- console.setFormatter(formatter)
- # add the handler to the root logger
- logger.addHandler(console)
- logger.setLevel(logging.INFO)
-
- bind("pymarc", "http://pypi.python.org/packages/2.5/p/pymarc/pymarc-2.1-py2.5.egg")
-
- import pymarc
-
- print pymarc.__version__, pymarc.__file__
-
- assert pymarc.__version__=="2.1"
-
- d = _getImporter().cache.directory
- assert d in pymarc.__file__, "'%s' not found in pymarc.__file__ (%s)" % (d, pymarc.__file__)
-
- # Can now also bind to non top level packages. The packages
- # leading up to the package being bound will need to be defined
- # however.
- #
- # bind("rdf.plugins.stores.memory",
- # "http://pypi.python.org/packages/2.5/r/rdf.plugins.stores.memeory/rdf.plugins.stores.memory-0.9a-py2.5.egg")
- #
- # from rdf.plugins.stores.memory import Memory
-
-
diff --git a/WebKitTools/Scripts/webkitpy/buildbot.py b/WebKitTools/Scripts/webkitpy/buildbot.py
deleted file mode 100644
index 38828fd..0000000
--- a/WebKitTools/Scripts/webkitpy/buildbot.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# Copyright (c) 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.
-#
-# WebKit's Python module for interacting with WebKit's buildbot
-
-import re
-import urllib2
-
-# Import WebKit-specific modules.
-from webkitpy.webkit_logging import log
-
-# WebKit includes a built copy of BeautifulSoup in Scripts/webkitpy
-# so this import should always succeed.
-from .BeautifulSoup import BeautifulSoup
-
-
-class BuildBot:
-
- default_host = "build.webkit.org"
-
- def __init__(self, host=default_host):
- self.buildbot_host = host
- self.buildbot_server_url = "http://%s/" % self.buildbot_host
-
- # If any Leopard builder/tester, Windows builder or Chromium builder is
- # red we should not be landing patches. Other builders should be added
- # to this list once they are known to be reliable.
- # See https://bugs.webkit.org/show_bug.cgi?id=33296 and related bugs.
- self.core_builder_names_regexps = [
- "Leopard",
- "Windows.*Build",
- "Chromium",
- ]
-
- def _parse_builder_status_from_row(self, status_row):
- # If WebKit's buildbot has an XMLRPC interface we could use, we could
- # do something more sophisticated here. For now we just parse out the
- # basics, enough to support basic questions like "is the tree green?"
- status_cells = status_row.findAll('td')
- builder = {}
-
- name_link = status_cells[0].find('a')
- builder['name'] = name_link.string
- # We could generate the builder_url from the name in a future version
- # of this code.
- builder['builder_url'] = self.buildbot_server_url + name_link['href']
-
- status_link = status_cells[1].find('a')
- if not status_link:
- # We failed to find a link in the first cell, just give up. This
- # can happen if a builder is just-added, the first cell will just
- # be "no build"
- # Other parts of the code depend on is_green being present.
- builder['is_green'] = False
- return builder
- # Will be either a revision number or a build number
- revision_string = status_link.string
- # If revision_string has non-digits assume it's not a revision number.
- builder['built_revision'] = int(revision_string) \
- if not re.match('\D', revision_string) \
- else None
- builder['is_green'] = not re.search('fail',
- status_cells[1].renderContents())
- # We could parse out the build number instead, but for now just store
- # the URL.
- builder['build_url'] = self.buildbot_server_url + status_link['href']
-
- # We could parse out the current activity too.
-
- return builder
-
- def _builder_statuses_with_names_matching_regexps(self,
- builder_statuses,
- name_regexps):
- builders = []
- for builder in builder_statuses:
- for name_regexp in name_regexps:
- if re.match(name_regexp, builder['name']):
- builders.append(builder)
- return builders
-
- 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']:
- 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'
- page = urllib2.urlopen(build_status_url)
- soup = BeautifulSoup(page)
-
- builders = []
- status_table = soup.find('table')
- for status_row in status_table.findAll('tr'):
- builder = self._parse_builder_status_from_row(status_row)
- builders.append(builder)
- return builders
diff --git a/WebKitTools/Scripts/webkitpy/buildbot_unittest.py b/WebKitTools/Scripts/webkitpy/buildbot_unittest.py
deleted file mode 100644
index bde3e04..0000000
--- a/WebKitTools/Scripts/webkitpy/buildbot_unittest.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (C) 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.
-
-import unittest
-
-from webkitpy.buildbot import BuildBot
-
-from webkitpy.BeautifulSoup import BeautifulSoup
-
-class BuildBotTest(unittest.TestCase):
-
- _example_one_box_status = '''
- <table>
- <tr>
- <td class="box"><a href="builders/Windows%20Debug%20%28Tests%29">Windows Debug (Tests)</a></td>
- <td align="center" class="LastBuild box success"><a href="builders/Windows%20Debug%20%28Tests%29/builds/3693">47380</a><br />build<br />successful</td>
- <td align="center" class="Activity building">building<br />ETA in<br />~ 14 mins<br />at 13:40</td>
- <tr>
- <td class="box"><a href="builders/SnowLeopard%20Intel%20Release">SnowLeopard Intel Release</a></td>
- <td class="LastBuild box" >no build</td>
- <td align="center" class="Activity building">building<br />< 1 min</td>
- <tr>
- <td class="box"><a href="builders/Qt%20Linux%20Release">Qt Linux Release</a></td>
- <td align="center" class="LastBuild box failure"><a href="builders/Qt%20Linux%20Release/builds/654">47383</a><br />failed<br />compile-webkit</td>
- <td align="center" class="Activity idle">idle</td>
- </table>
-'''
- _expected_example_one_box_parsings = [
- {
- 'builder_url': u'http://build.webkit.org/builders/Windows%20Debug%20%28Tests%29',
- 'build_url': u'http://build.webkit.org/builders/Windows%20Debug%20%28Tests%29/builds/3693',
- 'is_green': True,
- 'name': u'Windows Debug (Tests)',
- 'built_revision': 47380
- },
- {
- 'builder_url': u'http://build.webkit.org/builders/SnowLeopard%20Intel%20Release',
- 'is_green': False,
- 'name': u'SnowLeopard Intel Release',
- },
- {
- 'builder_url': u'http://build.webkit.org/builders/Qt%20Linux%20Release',
- 'build_url': u'http://build.webkit.org/builders/Qt%20Linux%20Release/builds/654',
- 'is_green': False,
- 'name': u'Qt Linux Release',
- 'built_revision': 47383
- },
- ]
-
- def test_status_parsing(self):
- buildbot = BuildBot()
-
- soup = BeautifulSoup(self._example_one_box_status)
- status_table = soup.find("table")
- input_rows = status_table.findAll('tr')
-
- for x in range(len(input_rows)):
- status_row = input_rows[x]
- expected_parsing = self._expected_example_one_box_parsings[x]
-
- builder = buildbot._parse_builder_status_from_row(status_row)
-
- # Make sure we aren't parsing more or less than we expect
- self.assertEquals(builder.keys(), expected_parsing.keys())
-
- 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()
-
- # For complete testing, this list should match the list of builders at build.webkit.org:
- example_builders = [
- { 'name': u'Tiger Intel Release', },
- { 'name': u'Leopard Intel Release (Build)', },
- { 'name': u'Leopard Intel Release (Tests)', },
- { 'name': u'Leopard Intel Debug (Build)', },
- { 'name': u'Leopard Intel Debug (Tests)', },
- { 'name': u'SnowLeopard Intel Release (Build)', },
- { 'name': u'SnowLeopard Intel Release (Tests)', },
- { 'name': u'SnowLeopard Intel Leaks', },
- { 'name': u'Windows Release (Build)', },
- { 'name': u'Windows Release (Tests)', },
- { 'name': u'Windows Debug (Build)', },
- { 'name': u'Windows Debug (Tests)', },
- { 'name': u'Qt Linux Release', },
- { 'name': u'Gtk Linux Release', },
- { 'name': u'Gtk Linux 32-bit Debug', },
- { 'name': u'Gtk Linux 64-bit Debug', },
- { 'name': u'Chromium Linux Release', },
- { 'name': u'Chromium Mac Release', },
- { 'name': u'Chromium Win Release', },
- ]
- name_regexps = [ "Leopard", "Windows.*Build", "Chromium" ]
- expected_builders = [
- { 'name': u'Leopard Intel Release (Build)', },
- { 'name': u'Leopard Intel Release (Tests)', },
- { 'name': u'Leopard Intel Debug (Build)', },
- { 'name': u'Leopard Intel Debug (Tests)', },
- { 'name': u'Windows Release (Build)', },
- { 'name': u'Windows Debug (Build)', },
- { 'name': u'Chromium Linux Release', },
- { 'name': u'Chromium Mac Release', },
- { 'name': u'Chromium Win Release', },
- ]
-
- # This test should probably be updated if the default regexp list changes
- self.assertEquals(buildbot.core_builder_names_regexps, name_regexps)
-
- builders = buildbot._builder_statuses_with_names_matching_regexps(example_builders, name_regexps)
- self.assertEquals(builders, expected_builders)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/commands/queries.py b/WebKitTools/Scripts/webkitpy/commands/queries.py
deleted file mode 100644
index 3ca4f42..0000000
--- a/WebKitTools/Scripts/webkitpy/commands/queries.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
-# 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:
-#
-# * 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 optparse import make_option
-
-from webkitpy.buildbot import BuildBot
-from webkitpy.committers import CommitterList
-from webkitpy.webkit_logging import log
-from webkitpy.multicommandtool import AbstractDeclarativeCommand
-
-
-class BugsToCommit(AbstractDeclarativeCommand):
- name = "bugs-to-commit"
- help_text = "List bugs in the commit-queue"
-
- def execute(self, options, args, tool):
- # FIXME: This command is poorly named. It's fetching the commit-queue list here. The name implies it's fetching pending-commit (all r+'d patches).
- bug_ids = tool.bugs.queries.fetch_bug_ids_from_commit_queue()
- for bug_id in bug_ids:
- print "%s" % bug_id
-
-
-class PatchesInCommitQueue(AbstractDeclarativeCommand):
- name = "patches-in-commit-queue"
- help_text = "List patches in the commit-queue"
-
- def execute(self, options, args, tool):
- patches = tool.bugs.queries.fetch_patches_from_commit_queue()
- log("Patches in commit queue:")
- for patch in patches:
- print patch.url()
-
-
-class PatchesToCommitQueue(AbstractDeclarativeCommand):
- name = "patches-to-commit-queue"
- help_text = "Patches which should be added to the commit queue"
- def __init__(self):
- options = [
- make_option("--bugs", action="store_true", dest="bugs", help="Output bug links instead of patch links"),
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- @staticmethod
- def _needs_commit_queue(patch):
- if patch.commit_queue() == "+": # If it's already cq+, ignore the patch.
- log("%s already has cq=%s" % (patch.id(), patch.commit_queue()))
- return False
-
- # We only need to worry about patches from contributers who are not yet committers.
- committer_record = CommitterList().committer_by_email(patch.attacher_email())
- if committer_record:
- log("%s committer = %s" % (patch.id(), committer_record))
- return not committer_record
-
- def execute(self, options, args, tool):
- patches = tool.bugs.queries.fetch_patches_from_pending_commit_list()
- patches_needing_cq = filter(self._needs_commit_queue, patches)
- if options.bugs:
- bugs_needing_cq = map(lambda patch: patch.bug_id(), patches_needing_cq)
- bugs_needing_cq = sorted(set(bugs_needing_cq))
- for bug_id in bugs_needing_cq:
- print "%s" % tool.bugs.bug_url_for_bug_id(bug_id)
- else:
- for patch in patches_needing_cq:
- print "%s" % tool.bugs.attachment_url_for_id(patch.id(), action="edit")
-
-
-class PatchesToReview(AbstractDeclarativeCommand):
- name = "patches-to-review"
- help_text = "List patches that are pending review"
-
- def execute(self, options, args, tool):
- patch_ids = tool.bugs.queries.fetch_attachment_ids_from_review_queue()
- log("Patches pending review:")
- for patch_id in patch_ids:
- print patch_id
-
-
-class TreeStatus(AbstractDeclarativeCommand):
- name = "tree-status"
- help_text = "Print the status of the %s buildbots" % BuildBot.default_host
- long_help = """Fetches build status from http://build.webkit.org/one_box_per_builder
-and displayes the status of each builder."""
-
- def execute(self, options, args, tool):
- for builder in tool.buildbot.builder_statuses():
- status_string = "ok" if builder["is_green"] else "FAIL"
- print "%s : %s" % (status_string.ljust(4), builder["name"])
diff --git a/WebKitTools/Scripts/webkitpy/commands/__init__.py b/WebKitTools/Scripts/webkitpy/common/__init__.py
index ef65bee..ef65bee 100644
--- a/WebKitTools/Scripts/webkitpy/commands/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/common/__init__.py
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py b/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/api.py b/WebKitTools/Scripts/webkitpy/common/checkout/api.py
new file mode 100644
index 0000000..c4e2b69
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/api.py
@@ -0,0 +1,140 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import subprocess
+import StringIO
+
+from webkitpy.common.checkout.changelog import ChangeLog
+from webkitpy.common.checkout.commitinfo import CommitInfo
+from webkitpy.common.checkout.scm import CommitMessage
+from webkitpy.common.net.bugzilla import parse_bug_id
+from webkitpy.common.system.executive import Executive, run_command, ScriptError
+from webkitpy.common.system.deprecated_logging import log
+
+
+# This class represents the WebKit-specific parts of the checkout (like
+# ChangeLogs).
+# FIXME: Move a bunch of ChangeLog-specific processing from SCM to this object.
+class Checkout(object):
+ def __init__(self, scm):
+ self._scm = scm
+
+ def _is_path_to_changelog(self, path):
+ return os.path.basename(path) == "ChangeLog"
+
+ def _latest_entry_for_changelog_at_revision(self, changelog_path, revision):
+ changelog_contents = self._scm.contents_at_revision(changelog_path, revision)
+ return ChangeLog.parse_latest_entry_from_file(StringIO.StringIO(changelog_contents))
+
+ def changelog_entries_for_revision(self, revision):
+ changed_files = self._scm.changed_files_for_revision(revision)
+ return [self._latest_entry_for_changelog_at_revision(path, revision) for path in changed_files if self._is_path_to_changelog(path)]
+
+ def commit_info_for_revision(self, revision):
+ committer_email = self._scm.committer_email_for_revision(revision)
+ changelog_entries = self.changelog_entries_for_revision(revision)
+ # Assume for now that the first entry has everything we need:
+ # FIXME: This will throw an exception if there were no ChangeLogs.
+ if not len(changelog_entries):
+ return None
+ changelog_entry = changelog_entries[0]
+ changelog_data = {
+ "bug_id": parse_bug_id(changelog_entry.contents()),
+ "author_name": changelog_entry.author_name(),
+ "author_email": changelog_entry.author_email(),
+ "author": changelog_entry.author(),
+ "reviewer_text": changelog_entry.reviewer_text(),
+ "reviewer": changelog_entry.reviewer(),
+ }
+ # We could pass the changelog_entry instead of a dictionary here, but that makes
+ # mocking slightly more involved, and would make aggregating data from multiple
+ # entries more difficult to wire in if we need to do that in the future.
+ return CommitInfo(revision, committer_email, changelog_data)
+
+ def bug_id_for_revision(self, revision):
+ return self.commit_info_for_revision(revision).bug_id()
+
+ def modified_changelogs(self):
+ # SCM returns paths relative to scm.checkout_root
+ # Callers (especially those using the ChangeLog class) may
+ # expect absolute paths, so this method returns absolute paths.
+ changed_files = self._scm.changed_files()
+ absolute_paths = [os.path.join(self._scm.checkout_root, path) for path in changed_files]
+ return [path for path in absolute_paths if self._is_path_to_changelog(path)]
+
+ def commit_message_for_this_commit(self):
+ changelog_paths = self.modified_changelogs()
+ if not len(changelog_paths):
+ raise ScriptError(message="Found no modified ChangeLogs, cannot create a commit message.\n"
+ "All changes require a ChangeLog. See:\n"
+ "http://webkit.org/coding/contributing.html")
+
+ changelog_messages = []
+ for changelog_path in changelog_paths:
+ log("Parsing ChangeLog: %s" % changelog_path)
+ changelog_entry = ChangeLog(changelog_path).latest_entry()
+ if not changelog_entry:
+ raise ScriptError(message="Failed to parse ChangeLog: %s" % os.path.abspath(changelog_path))
+ changelog_messages.append(changelog_entry.contents())
+
+ # FIXME: We should sort and label the ChangeLog messages like commit-log-editor does.
+ return CommitMessage("".join(changelog_messages).splitlines())
+
+ def bug_id_for_this_commit(self):
+ try:
+ return parse_bug_id(self.commit_message_for_this_commit().message())
+ except ScriptError, e:
+ pass # We might not have ChangeLogs.
+
+ def apply_patch(self, patch, force=False):
+ # It's possible that the patch was not made from the root directory.
+ # We should detect and handle that case.
+ # FIXME: Use Executive instead of subprocess here.
+ curl_process = subprocess.Popen(['curl', '--location', '--silent', '--show-error', patch.url()], stdout=subprocess.PIPE)
+ # FIXME: Move _scm.script_path here once we get rid of all the dependencies.
+ args = [self._scm.script_path('svn-apply')]
+ if patch.reviewer():
+ args += ['--reviewer', patch.reviewer().full_name]
+ if force:
+ args.append('--force')
+
+ run_command(args, input=curl_process.stdout)
+
+ def apply_reverse_diff(self, revision):
+ self._scm.apply_reverse_diff(revision)
+
+ # We revert the ChangeLogs because removing lines from a ChangeLog
+ # doesn't make sense. ChangeLogs are append only.
+ changelog_paths = self.modified_changelogs()
+ if len(changelog_paths):
+ self._scm.revert_files(changelog_paths)
+
+ conflicts = self._scm.conflicted_files()
+ if len(conflicts):
+ raise ScriptError(message="Failed to apply reverse diff for revision %s because of the following conflicts:\n%s" % (revision, "\n".join(conflicts)))
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py
new file mode 100644
index 0000000..e99caee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py
@@ -0,0 +1,169 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from webkitpy.common.checkout.api import Checkout
+from webkitpy.common.checkout.changelog import ChangeLogEntry
+from webkitpy.common.checkout.scm import detect_scm_system, CommitMessage
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+
+# FIXME: Copied from scm_unittest.py
+def write_into_file_at_path(file_path, contents):
+ new_file = open(file_path, 'w')
+ new_file.write(contents)
+ new_file.close()
+
+
+_changelog1entry1 = """2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Unreviewed build fix to un-break webkit-patch land.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
+"""
+_changelog1entry2 = """2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ * Scripts/webkitpy/common/checkout/api.py:
+"""
+_changelog1 = "\n".join([_changelog1entry1, _changelog1entry2])
+_changelog2 = """2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Unreviewed build fix to un-break webkit-patch land.
+
+ Second part of this complicated change.
+
+ * Path/To/Complicated/File: Added.
+
+2010-03-25 Adam Barth <abarth@webkit.org>
+
+ Reviewed by Eric Seidel.
+
+ Filler change.
+"""
+
+class CommitMessageForThisCommitTest(unittest.TestCase):
+ expected_commit_message = """2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Unreviewed build fix to un-break webkit-patch land.
+
+ Move commit_message_for_this_commit from scm to checkout
+ https://bugs.webkit.org/show_bug.cgi?id=36629
+
+ * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
+2010-03-25 Eric Seidel <eric@webkit.org>
+
+ Unreviewed build fix to un-break webkit-patch land.
+
+ Second part of this complicated change.
+
+ * Path/To/Complicated/File: Added.
+"""
+
+ def setUp(self):
+ self.temp_dir = tempfile.mkdtemp(suffix="changelogs")
+ self.old_cwd = os.getcwd()
+ os.chdir(self.temp_dir)
+ write_into_file_at_path("ChangeLog1", _changelog1)
+ write_into_file_at_path("ChangeLog2", _changelog2)
+
+ def tearDown(self):
+ shutil.rmtree(self.temp_dir, ignore_errors=True)
+ os.chdir(self.old_cwd)
+
+ # FIXME: This should not need to touch the file system, however
+ # ChangeLog is difficult to mock at current.
+ def test_commit_message_for_this_commit(self):
+ checkout = Checkout(None)
+ checkout.modified_changelogs = lambda: ["ChangeLog1", "ChangeLog2"]
+ output = OutputCapture()
+ expected_stderr = "Parsing ChangeLog: ChangeLog1\nParsing ChangeLog: ChangeLog2\n"
+ commit_message = output.assert_outputs(self, checkout.commit_message_for_this_commit, expected_stderr=expected_stderr)
+ self.assertEqual(commit_message.message(), self.expected_commit_message)
+
+
+class CheckoutTest(unittest.TestCase):
+ def test_latest_entry_for_changelog_at_revision(self):
+ scm = Mock()
+ def mock_contents_at_revision(changelog_path, revision):
+ self.assertEqual(changelog_path, "foo")
+ self.assertEqual(revision, "bar")
+ return _changelog1
+ scm.contents_at_revision = mock_contents_at_revision
+ checkout = Checkout(scm)
+ entry = checkout._latest_entry_for_changelog_at_revision("foo", "bar")
+ self.assertEqual(entry.contents(), _changelog1entry1)
+
+ def test_commit_info_for_revision(self):
+ scm = Mock()
+ scm.committer_email_for_revision = lambda revision: "committer@example.com"
+ checkout = Checkout(scm)
+ checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)]
+ commitinfo = checkout.commit_info_for_revision(4)
+ self.assertEqual(commitinfo.bug_id(), 36629)
+ self.assertEqual(commitinfo.author_name(), "Eric Seidel")
+ self.assertEqual(commitinfo.author_email(), "eric@webkit.org")
+ self.assertEqual(commitinfo.reviewer_text(), None)
+ self.assertEqual(commitinfo.reviewer(), None)
+ self.assertEqual(commitinfo.committer_email(), "committer@example.com")
+ self.assertEqual(commitinfo.committer(), None)
+
+ checkout.changelog_entries_for_revision = lambda revision: []
+ self.assertEqual(checkout.commit_info_for_revision(1), None)
+
+ def test_bug_id_for_revision(self):
+ scm = Mock()
+ scm.committer_email_for_revision = lambda revision: "committer@example.com"
+ checkout = Checkout(scm)
+ checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)]
+ self.assertEqual(checkout.bug_id_for_revision(4), 36629)
+
+ def test_bug_id_for_this_commit(self):
+ scm = Mock()
+ checkout = Checkout(scm)
+ checkout.commit_message_for_this_commit = lambda: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines())
+ self.assertEqual(checkout.bug_id_for_this_commit(), 36629)
+
+ def test_modified_changelogs(self):
+ scm = Mock()
+ scm.checkout_root = "/foo/bar"
+ scm.changed_files = lambda:["file1", "ChangeLog", "relative/path/ChangeLog"]
+ checkout = Checkout(scm)
+ expected_changlogs = ["/foo/bar/ChangeLog", "/foo/bar/relative/path/ChangeLog"]
+ self.assertEqual(checkout.modified_changelogs(), expected_changlogs)
diff --git a/WebKitTools/Scripts/webkitpy/changelogs.py b/WebKitTools/Scripts/webkitpy/common/checkout/changelog.py
index ebc89c4..e93896f 100644
--- a/WebKitTools/Scripts/webkitpy/changelogs.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/changelog.py
@@ -28,10 +28,14 @@
#
# WebKit's Python module for parsing and modifying ChangeLog files
+import codecs
import fileinput # inplace file editing for set_reviewer_in_changelog
+import os.path
import re
import textwrap
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.config.committers import CommitterList
def view_source_url(revision_number):
# FIMXE: This doesn't really belong in this file, but we don't have a
@@ -40,39 +44,82 @@ def view_source_url(revision_number):
return "http://trac.webkit.org/changeset/%s" % revision_number
-class ChangeLog:
+class ChangeLogEntry(object):
+ # e.g. 2009-06-03 Eric Seidel <eric@webkit.org>
+ date_line_regexp = r'^(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<name>.+?)\s+<(?P<email>[^<>]+)>$'
+
+ def __init__(self, contents, committer_list=CommitterList()):
+ self._contents = contents
+ self._committer_list = committer_list
+ self._parse_entry()
+
+ def _parse_entry(self):
+ match = re.match(self.date_line_regexp, self._contents, re.MULTILINE)
+ if not match:
+ log("WARNING: Creating invalid ChangeLogEntry:\n%s" % self._contents)
+
+ # FIXME: group("name") does not seem to be Unicode? Probably due to self._contents not being unicode.
+ self._author_name = match.group("name") if match else None
+ self._author_email = match.group("email") if match else None
+
+ match = re.search("^\s+Reviewed by (?P<reviewer>.*?)[\.,]?\s*$", self._contents, re.MULTILINE) # Discard everything after the first period
+ self._reviewer_text = match.group("reviewer") if match else None
+
+ self._reviewer = self._committer_list.committer_by_name(self._reviewer_text)
+ self._author = self._committer_list.committer_by_email(self._author_email) or self._committer_list.committer_by_name(self._author_name)
+
+ def author_name(self):
+ return self._author_name
+
+ def author_email(self):
+ return self._author_email
+
+ def author(self):
+ return self._author # Might be None
+
+ # FIXME: Eventually we would like to map reviwer names to reviewer objects.
+ # See https://bugs.webkit.org/show_bug.cgi?id=26533
+ def reviewer_text(self):
+ return self._reviewer_text
+
+ def reviewer(self):
+ return self._reviewer # Might be None
+
+ def contents(self):
+ return self._contents
+
+
+# FIXME: Various methods on ChangeLog should move into ChangeLogEntry instead.
+class ChangeLog(object):
def __init__(self, path):
self.path = path
_changelog_indent = " " * 8
- # e.g. 2009-06-03 Eric Seidel <eric@webkit.org>
- date_line_regexp = re.compile('^(\d{4}-\d{2}-\d{2})' # Consume the date.
- + '\s+(.+)\s+' # Consume the name.
- + '<([^<>]+)>$') # And the email address.
-
@staticmethod
- def _parse_latest_entry_from_file(changelog_file):
+ def parse_latest_entry_from_file(changelog_file):
+ date_line_regexp = re.compile(ChangeLogEntry.date_line_regexp)
entry_lines = []
# The first line should be a date line.
first_line = changelog_file.readline()
- if not ChangeLog.date_line_regexp.match(first_line):
+ if not date_line_regexp.match(first_line):
return None
entry_lines.append(first_line)
for line in changelog_file:
# If we've hit the next entry, return.
- if ChangeLog.date_line_regexp.match(line):
+ if date_line_regexp.match(line):
# Remove the extra newline at the end
- return ''.join(entry_lines[:-1])
+ return ChangeLogEntry(''.join(entry_lines[:-1]))
entry_lines.append(line)
return None # We never found a date line!
def latest_entry(self):
- changelog_file = open(self.path)
+ # ChangeLog files are always UTF-8, we read them in as such to support Reviewers with unicode in their names.
+ changelog_file = codecs.open(self.path, "r", "utf-8")
try:
- return self._parse_latest_entry_from_file(changelog_file)
+ return self.parse_latest_entry_from_file(changelog_file)
finally:
changelog_file.close()
@@ -96,7 +143,7 @@ class ChangeLog:
# This probably does not belong in changelogs.py
def _message_for_revert(self, revision, reason, bug_url):
- message = "No review, rolling out r%s.\n" % revision
+ message = "Unreviewed, rolling out r%s.\n" % revision
message += "%s\n" % view_source_url(revision)
if bug_url:
message += "%s\n" % bug_url
diff --git a/WebKitTools/Scripts/webkitpy/changelogs_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py
index de3e60c..9210c9c 100644
--- a/WebKitTools/Scripts/webkitpy/changelogs_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py
@@ -27,17 +27,19 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
-from changelogs import *
-
import os
import tempfile
+
from StringIO import StringIO
+from webkitpy.common.checkout.changelog import *
+
+
class ChangeLogsTest(unittest.TestCase):
- _example_entry = '''2009-08-17 Peter Kasting <pkasting@google.com>
+ _example_entry = u'''2009-08-17 Peter Kasting <pkasting@google.com>
- Reviewed by Steve Falkenburg.
+ Reviewed by Tor Arne Vestb\xf8.
https://bugs.webkit.org/show_bug.cgi?id=27323
Only add Cygwin to the path when it isn't already there. This avoids
@@ -87,8 +89,12 @@ class ChangeLogsTest(unittest.TestCase):
def test_latest_entry_parse(self):
changelog_contents = "%s\n%s" % (self._example_entry, self._example_changelog)
changelog_file = StringIO(changelog_contents)
- latest_entry = ChangeLog._parse_latest_entry_from_file(changelog_file)
- self.assertEquals(self._example_entry, latest_entry)
+ latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
+ self.assertEquals(latest_entry.contents(), self._example_entry)
+ self.assertEquals(latest_entry.author_name(), "Peter Kasting")
+ self.assertEquals(latest_entry.author_email(), "pkasting@google.com")
+ self.assertEquals(latest_entry.reviewer_text(), u"Tor Arne Vestb\xf8")
+ self.assertTrue(latest_entry.reviewer()) # Make sure that our UTF8-based lookup of Tor works.
@staticmethod
def _write_tmp_file_with_contents(contents):
@@ -124,7 +130,7 @@ class ChangeLogsTest(unittest.TestCase):
os.remove(changelog_path)
self.assertEquals(actual_contents, expected_contents)
- _revert_message = """ No review, rolling out r12345.
+ _revert_message = """ Unreviewed, rolling out r12345.
http://trac.webkit.org/changeset/12345
http://example.com/123
@@ -143,7 +149,7 @@ class ChangeLogsTest(unittest.TestCase):
_revert_entry_with_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
- No review, rolling out r12345.
+ Unreviewed, rolling out r12345.
http://trac.webkit.org/changeset/12345
http://example.com/123
@@ -154,7 +160,7 @@ class ChangeLogsTest(unittest.TestCase):
_revert_entry_without_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
- No review, rolling out r12345.
+ Unreviewed, rolling out r12345.
http://trac.webkit.org/changeset/12345
Reason
@@ -169,11 +175,16 @@ class ChangeLogsTest(unittest.TestCase):
changelog.update_for_revert(*args)
actual_entry = changelog.latest_entry()
os.remove(changelog_path)
- self.assertEquals(actual_entry, expected_entry)
+ self.assertEquals(actual_entry.contents(), expected_entry)
+ self.assertEquals(actual_entry.reviewer_text(), None)
+ # These checks could be removed to allow this to work on other entries:
+ self.assertEquals(actual_entry.author_name(), "Eric Seidel")
+ self.assertEquals(actual_entry.author_email(), "eric@webkit.org")
def test_update_for_revert(self):
self._assert_update_for_revert_output([12345, "Reason"], self._revert_entry_without_bug_url)
self._assert_update_for_revert_output([12345, "Reason", "http://example.com/123"], self._revert_entry_with_bug_url)
+
if __name__ == '__main__':
unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py
new file mode 100644
index 0000000..7c3315f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2010 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.
+#
+# WebKit's python module for holding information on a commit
+
+import StringIO
+
+from webkitpy.common.checkout.changelog import view_source_url
+from webkitpy.common.config.committers import CommitterList
+
+
+class CommitInfo(object):
+ def __init__(self, revision, committer_email, changelog_data, committer_list=CommitterList()):
+ self._revision = revision
+ self._committer_email = committer_email
+ self._bug_id = changelog_data["bug_id"]
+ self._author_name = changelog_data["author_name"]
+ self._author_email = changelog_data["author_email"]
+ self._author = changelog_data["author"]
+ self._reviewer_text = changelog_data["reviewer_text"]
+ self._reviewer = changelog_data["reviewer"]
+
+ # Derived values:
+ self._committer = committer_list.committer_by_email(committer_email)
+
+ def revision(self):
+ return self._revision
+
+ def committer(self):
+ return self._committer # None if committer isn't in committers.py
+
+ def committer_email(self):
+ return self._committer_email
+
+ def bug_id(self):
+ return self._bug_id # May be None
+
+ def author(self):
+ return self._author # May be None
+
+ def author_name(self):
+ return self._author_name
+
+ def author_email(self):
+ return self._author_email
+
+ def reviewer(self):
+ return self._reviewer # May be None
+
+ def reviewer_text(self):
+ return self._reviewer_text # May be None
+
+ def responsible_parties(self):
+ responsible_parties = [
+ self.committer(),
+ self.author(),
+ self.reviewer(),
+ ]
+ return set([party for party in responsible_parties if party]) # Filter out None
+
+ # FIXME: It is slightly lame that this "view" method is on this "model" class (in MVC terms)
+ def blame_string(self, bugs):
+ string = "r%s:\n" % self.revision()
+ string += " %s\n" % view_source_url(self.revision())
+ string += " Bug: %s (%s)\n" % (self.bug_id(), bugs.bug_url_for_bug_id(self.bug_id()))
+ author_line = "\"%s\" <%s>" % (self.author_name(), self.author_email())
+ string += " Author: %s\n" % (self.author() or author_line)
+ string += " Reviewer: %s\n" % (self.reviewer() or self.reviewer_text())
+ string += " Committer: %s" % self.committer()
+ return string
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py
new file mode 100644
index 0000000..f58e6f1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py
@@ -0,0 +1,61 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.checkout.commitinfo import CommitInfo
+from webkitpy.common.config.committers import CommitterList, Committer, Reviewer
+
+class CommitInfoTest(unittest.TestCase):
+
+ def test_commit_info_creation(self):
+ author = Committer("Author", "author@example.com")
+ committer = Committer("Committer", "committer@example.com")
+ reviewer = Reviewer("Reviewer", "reviewer@example.com")
+ committer_list = CommitterList(committers=[author, committer], reviewers=[reviewer])
+
+ changelog_data = {
+ "bug_id": 1234,
+ "author_name": "Committer",
+ "author_email": "author@example.com",
+ "author": author,
+ "reviewer_text": "Reviewer",
+ "reviewer": reviewer,
+ }
+ commit = CommitInfo(123, "committer@example.com", changelog_data, committer_list)
+
+ self.assertEqual(commit.revision(), 123)
+ self.assertEqual(commit.bug_id(), 1234)
+ self.assertEqual(commit.author_name(), "Committer")
+ self.assertEqual(commit.author_email(), "author@example.com")
+ self.assertEqual(commit.author(), author)
+ self.assertEqual(commit.reviewer_text(), "Reviewer")
+ self.assertEqual(commit.reviewer(), reviewer)
+ self.assertEqual(commit.committer(), committer)
+ self.assertEqual(commit.committer_email(), "committer@example.com")
+ self.assertEqual(commit.responsible_parties(), set([author, committer, reviewer]))
diff --git a/WebKitTools/Scripts/webkitpy/diff_parser.py b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py
index 7dce7e8..d8ebae6 100644
--- a/WebKitTools/Scripts/webkitpy/diff_parser.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py
@@ -31,6 +31,7 @@
import logging
import re
+_log = logging.getLogger("webkitpy.common.checkout.diff_parser")
_regexp_compile_cache = {}
@@ -138,7 +139,8 @@ class DiffParser:
lines_changed = match(r"^@@ -(?P<OldStartLine>\d+)(,\d+)? \+(?P<NewStartLine>\d+)(,\d+)? @@", line)
if lines_changed:
if state != _DECLARED_FILE_PATH and state != _PROCESSING_CHUNK:
- logging.error('Unexpected line change without file path declaration: %r' % line)
+ _log.error('Unexpected line change without file path '
+ 'declaration: %r' % line)
old_diff_line = int(lines_changed.group('OldStartLine'))
new_diff_line = int(lines_changed.group('NewStartLine'))
state = _PROCESSING_CHUNK
@@ -159,4 +161,5 @@ class DiffParser:
# Nothing to do. We may still have some added lines.
pass
else:
- logging.error('Unexpected diff format when parsing a chunk: %r' % line)
+ _log.error('Unexpected diff format when parsing a '
+ 'chunk: %r' % line)
diff --git a/WebKitTools/Scripts/webkitpy/diff_parser_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py
index 7eb0eab..7eb0eab 100644
--- a/WebKitTools/Scripts/webkitpy/diff_parser_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py
diff --git a/WebKitTools/Scripts/webkitpy/scm.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py
index 743f3fe..2704f07 100644
--- a/WebKitTools/Scripts/webkitpy/scm.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py
@@ -31,12 +31,14 @@
import os
import re
-import subprocess
-# Import WebKit-specific modules.
-from webkitpy.changelogs import ChangeLog
-from webkitpy.executive import Executive, run_command, ScriptError
-from webkitpy.webkit_logging import error, log
+# FIXME: Instead of using run_command directly, most places in this
+# class would rather use an SCM.run method which automatically set
+# cwd=self.checkout_root.
+from webkitpy.common.system.executive import Executive, run_command, ScriptError
+from webkitpy.common.system.user import User
+from webkitpy.common.system.deprecated_logging import error, log
+
def detect_scm_system(path):
if SVN.in_working_directory(path):
@@ -47,6 +49,7 @@ def detect_scm_system(path):
return None
+
def first_non_empty_line_after_index(lines, index=0):
first_non_empty_line = index
for line in lines[index:]:
@@ -90,20 +93,29 @@ def commit_error_handler(error):
Executive.default_error_handler(error)
+# SCM methods are expected to return paths relative to self.checkout_root.
class SCM:
- def __init__(self, cwd, dryrun=False):
+ def __init__(self, cwd):
self.cwd = cwd
self.checkout_root = self.find_checkout_root(self.cwd)
- self.dryrun = dryrun
+ self.dryrun = False
+ # SCM always returns repository relative path, but sometimes we need
+ # absolute paths to pass to rm, etc.
+ def absolute_path(self, repository_relative_path):
+ return os.path.join(self.checkout_root, repository_relative_path)
+
+ # FIXME: This belongs in Checkout, not SCM.
def scripts_directory(self):
return os.path.join(self.checkout_root, "WebKitTools", "Scripts")
+ # FIXME: This belongs in Checkout, not SCM.
def script_path(self, script_name):
return os.path.join(self.scripts_directory(), script_name)
def ensure_clean_working_directory(self, force_clean):
if not force_clean and not self.working_directory_is_clean():
+ # FIXME: Shouldn't this use cwd=self.checkout_root?
print run_command(self.status_command(), error_handler=Executive.ignore_error)
raise ScriptError(message="Working directory has modifications, pass --force-clean or --no-clean to continue.")
@@ -120,22 +132,10 @@ class SCM:
error("Working directory has local commits, pass --force-clean to continue.")
self.discard_local_commits()
- def apply_patch(self, patch, force=False):
- # It's possible that the patch was not made from the root directory.
- # We should detect and handle that case.
- # FIXME: scm.py should not deal with fetching Attachment data. Attachment should just have a .data() accessor.
- curl_process = subprocess.Popen(['curl', '--location', '--silent', '--show-error', patch.url()], stdout=subprocess.PIPE)
- args = [self.script_path('svn-apply')]
- if patch.reviewer():
- args += ['--reviewer', patch.reviewer().full_name]
- if force:
- args.append('--force')
-
- run_command(args, input=curl_process.stdout)
-
def run_status_and_extract_filenames(self, status_command, status_regexp):
filenames = []
- for line in run_command(status_command).splitlines():
+ # We run with cwd=self.checkout_root so that returned-paths are root-relative.
+ for line in run_command(status_command, cwd=self.checkout_root).splitlines():
match = re.search(status_regexp, line)
if not match:
continue
@@ -154,37 +154,6 @@ class SCM:
match = re.search(self.commit_success_regexp(), commit_text, re.MULTILINE)
return match.group('svn_revision')
- # ChangeLog-specific code doesn't really belong in scm.py, but this function is very useful.
- def modified_changelogs(self):
- changelog_paths = []
- paths = self.changed_files()
- for path in paths:
- if os.path.basename(path) == "ChangeLog":
- changelog_paths.append(path)
- return changelog_paths
-
- # FIXME: Requires unit test
- # FIXME: commit_message_for_this_commit and modified_changelogs don't
- # really belong here. We should have a separate module for
- # handling ChangeLogs.
- def commit_message_for_this_commit(self):
- changelog_paths = self.modified_changelogs()
- if not len(changelog_paths):
- raise ScriptError(message="Found no modified ChangeLogs, cannot create a commit message.\n"
- "All changes require a ChangeLog. See:\n"
- "http://webkit.org/coding/contributing.html")
-
- changelog_messages = []
- for changelog_path in changelog_paths:
- log("Parsing ChangeLog: %s" % changelog_path)
- changelog_entry = ChangeLog(changelog_path).latest_entry()
- if not changelog_entry:
- 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.
- return CommitMessage("".join(changelog_messages).splitlines())
-
@staticmethod
def in_working_directory(path):
raise NotImplementedError, "subclasses must implement"
@@ -206,15 +175,33 @@ class SCM:
def status_command(self):
raise NotImplementedError, "subclasses must implement"
+ def add(self, path):
+ raise NotImplementedError, "subclasses must implement"
+
def changed_files(self):
raise NotImplementedError, "subclasses must implement"
+ def changed_files_for_revision(self):
+ raise NotImplementedError, "subclasses must implement"
+
+ def added_files(self):
+ raise NotImplementedError, "subclasses must implement"
+
+ def conflicted_files(self):
+ raise NotImplementedError, "subclasses must implement"
+
def display_name(self):
raise NotImplementedError, "subclasses must implement"
def create_patch(self):
raise NotImplementedError, "subclasses must implement"
+ def committer_email_for_revision(self, revision):
+ raise NotImplementedError, "subclasses must implement"
+
+ def contents_at_revision(self, path, revision):
+ raise NotImplementedError, "subclasses must implement"
+
def diff_for_revision(self, revision):
raise NotImplementedError, "subclasses must implement"
@@ -224,7 +211,7 @@ class SCM:
def revert_files(self, file_paths):
raise NotImplementedError, "subclasses must implement"
- def commit_with_message(self, message):
+ def commit_with_message(self, message, username=None):
raise NotImplementedError, "subclasses must implement"
def svn_commit_log(self, svn_revision):
@@ -239,6 +226,9 @@ class SCM:
def supports_local_commits():
raise NotImplementedError, "subclasses must implement"
+ def svn_merge_base():
+ raise NotImplementedError, "subclasses must implement"
+
def create_patch_from_local_commit(self, commit_id):
error("Your source control manager does not support creating a patch from a local commit.")
@@ -256,8 +246,12 @@ class SCM:
class SVN(SCM):
- def __init__(self, cwd, dryrun=False):
- SCM.__init__(self, cwd, dryrun)
+ # FIXME: We should move these values to a WebKit-specific config. file.
+ svn_server_host = "svn.webkit.org"
+ svn_server_realm = "<http://svn.webkit.org:80> Mac OS Forge"
+
+ def __init__(self, cwd):
+ SCM.__init__(self, cwd)
self.cached_version = None
@staticmethod
@@ -299,6 +293,14 @@ class SVN(SCM):
def commit_success_regexp():
return "^Committed revision (?P<svn_revision>\d+)\.$"
+ def has_authorization_for_realm(self, realm=svn_server_realm, home_directory=os.getenv("HOME")):
+ # Assumes find and grep are installed.
+ if not os.path.isdir(os.path.join(home_directory, ".subversion")):
+ return False
+ find_args = ["find", ".subversion", "-type", "f", "-exec", "grep", "-q", realm, "{}", ";", "-print"];
+ find_output = run_command(find_args, cwd=home_directory, error_handler=Executive.ignore_error).rstrip()
+ return find_output and os.path.isfile(os.path.join(home_directory, find_output))
+
def svn_version(self):
if not self.cached_version:
self.cached_version = run_command(['svn', '--version', '--quiet'])
@@ -306,20 +308,50 @@ class SVN(SCM):
return self.cached_version
def working_directory_is_clean(self):
- return run_command(['svn', 'diff']) == ""
+ return run_command(["svn", "diff"], cwd=self.checkout_root) == ""
def clean_working_directory(self):
- run_command(['svn', 'revert', '-R', '.'])
+ # svn revert -R is not as awesome as git reset --hard.
+ # It will leave added files around, causing later svn update
+ # calls to fail on the bots. We make this mirror git reset --hard
+ # by deleting any added files as well.
+ added_files = reversed(sorted(self.added_files()))
+ # added_files() returns directories for SVN, we walk the files in reverse path
+ # length order so that we remove files before we try to remove the directories.
+ run_command(["svn", "revert", "-R", "."], cwd=self.checkout_root)
+ for path in added_files:
+ # This is robust against cwd != self.checkout_root
+ absolute_path = self.absolute_path(path)
+ # Completely lame that there is no easy way to remove both types with one call.
+ if os.path.isdir(path):
+ os.rmdir(absolute_path)
+ else:
+ os.remove(absolute_path)
def status_command(self):
return ['svn', 'status']
+ def _status_regexp(self, expected_types):
+ field_count = 6 if self.svn_version() > "1.6" else 5
+ return "^(?P<status>[%s]).{%s} (?P<filename>.+)$" % (expected_types, field_count)
+
+ def add(self, path):
+ # path is assumed to be cwd relative?
+ run_command(["svn", "add", path])
+
def changed_files(self):
- if self.svn_version() > "1.6":
- status_regexp = "^(?P<status>[ACDMR]).{6} (?P<filename>.+)$"
- else:
- status_regexp = "^(?P<status>[ACDMR]).{5} (?P<filename>.+)$"
- return self.run_status_and_extract_filenames(self.status_command(), status_regexp)
+ return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("ACDMR"))
+
+ def changed_files_for_revision(self, revision):
+ # As far as I can tell svn diff --summarize output looks just like svn status output.
+ status_command = ["svn", "diff", "--summarize", "-c", str(revision)]
+ return self.run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR"))
+
+ def conflicted_files(self):
+ return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("C"))
+
+ def added_files(self):
+ return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("A"))
@staticmethod
def supports_local_commits():
@@ -331,7 +363,15 @@ class SVN(SCM):
def create_patch(self):
return run_command(self.script_path("svn-create-patch"), cwd=self.checkout_root, return_stderr=False)
+ def committer_email_for_revision(self, revision):
+ return run_command(["svn", "propget", "svn:author", "--revprop", "-r", str(revision)]).rstrip()
+
+ def contents_at_revision(self, path, revision):
+ remote_path = "%s/%s" % (self._repository_url(), path)
+ return run_command(["svn", "cat", "-r", str(revision), remote_path])
+
def diff_for_revision(self, revision):
+ # FIXME: This should probably use cwd=self.checkout_root
return run_command(['svn', 'diff', '-c', str(revision)])
def _repository_url(self):
@@ -342,16 +382,27 @@ class SVN(SCM):
svn_merge_args = ['svn', 'merge', '--non-interactive', '-c', '-%s' % revision, self._repository_url()]
log("WARNING: svn merge has been known to take more than 10 minutes to complete. It is recommended you use git for rollouts.")
log("Running '%s'" % " ".join(svn_merge_args))
+ # FIXME: Should this use cwd=self.checkout_root?
run_command(svn_merge_args)
def revert_files(self, file_paths):
+ # FIXME: This should probably use cwd=self.checkout_root.
run_command(['svn', 'revert'] + file_paths)
- def commit_with_message(self, message):
+ def commit_with_message(self, message, username=None):
if self.dryrun:
# Return a string which looks like a commit so that things which parse this output will succeed.
return "Dry run, no commit.\nCommitted revision 0."
- return run_command(['svn', 'commit', '-m', message], error_handler=commit_error_handler)
+ svn_commit_args = ["svn", "commit"]
+ if not username and not self.has_authorization_for_realm():
+ username = User.prompt("%s login: " % self.svn_server_host, repeat=5)
+ if not username:
+ raise Exception("You need to specify the username on %s to perform the commit as." % self.svn_server_host)
+ if username:
+ svn_commit_args.extend(["--username", username])
+ svn_commit_args.extend(["-m", message])
+ # FIXME: Should this use cwd=self.checkout_root?
+ return run_command(svn_commit_args, error_handler=commit_error_handler)
def svn_commit_log(self, svn_revision):
svn_revision = self.strip_r_from_svn_revision(str(svn_revision))
@@ -364,8 +415,8 @@ class SVN(SCM):
# All git-specific logic should go here.
class Git(SCM):
- def __init__(self, cwd, dryrun=False):
- SCM.__init__(self, cwd, dryrun)
+ def __init__(self, cwd):
+ SCM.__init__(self, cwd)
@classmethod
def in_working_directory(cls, path):
@@ -379,25 +430,34 @@ class Git(SCM):
if not os.path.isabs(checkout_root): # Sometimes git returns relative paths
checkout_root = os.path.join(path, checkout_root)
return checkout_root
-
+
+ @classmethod
+ def read_git_config(cls, key):
+ # FIXME: This should probably use cwd=self.checkout_root.
+ return run_command(["git", "config", key],
+ error_handler=Executive.ignore_error).rstrip('\n')
+
@staticmethod
def commit_success_regexp():
return "^Committed r(?P<svn_revision>\d+)$"
-
def discard_local_commits(self):
- run_command(['git', 'reset', '--hard', 'trunk'])
+ # FIXME: This should probably use cwd=self.checkout_root
+ run_command(['git', 'reset', '--hard', self.svn_branch_name()])
def local_commits(self):
- return run_command(['git', 'log', '--pretty=oneline', 'HEAD...trunk']).splitlines()
+ # FIXME: This should probably use cwd=self.checkout_root
+ return run_command(['git', 'log', '--pretty=oneline', 'HEAD...' + self.svn_branch_name()]).splitlines()
def rebase_in_progress(self):
return os.path.exists(os.path.join(self.checkout_root, '.git/rebase-apply'))
def working_directory_is_clean(self):
- return run_command(['git', 'diff-index', 'HEAD']) == ""
+ # FIXME: This should probably use cwd=self.checkout_root
+ return run_command(['git', 'diff', 'HEAD', '--name-only']) == ""
def clean_working_directory(self):
+ # FIXME: These should probably use cwd=self.checkout_root.
# Could run git clean here too, but that wouldn't match working_directory_is_clean
run_command(['git', 'reset', '--hard', 'HEAD'])
# Aborting rebase even though this does not match working_directory_is_clean
@@ -405,13 +465,37 @@ class Git(SCM):
run_command(['git', 'rebase', '--abort'])
def status_command(self):
- return ['git', 'status']
+ # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead.
+ return ["git", "diff", "--name-status", "HEAD"]
+
+ def _status_regexp(self, expected_types):
+ return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types
+
+ def add(self, path):
+ # path is assumed to be cwd relative?
+ run_command(["git", "add", path])
def changed_files(self):
status_command = ['git', 'diff', '-r', '--name-status', '-C', '-M', 'HEAD']
- status_regexp = '^(?P<status>[ADM])\t(?P<filename>.+)$'
- return self.run_status_and_extract_filenames(status_command, status_regexp)
-
+ return self.run_status_and_extract_filenames(status_command, self._status_regexp("ADM"))
+
+ def _changes_files_for_commit(self, git_commit):
+ # --pretty="format:" makes git show not print the commit log header,
+ changed_files = run_command(["git", "show", "--pretty=format:", "--name-only", git_commit]).splitlines()
+ # instead it just prints a blank line at the top, so we skip the blank line:
+ return changed_files[1:]
+
+ def changed_files_for_revision(self, revision):
+ commit_id = self.git_commit_from_svn_revision(revision)
+ return self._changes_files_for_commit(commit_id)
+
+ def conflicted_files(self):
+ status_command = ['git', 'diff', '--name-status', '-C', '-M', '--diff-filter=U']
+ return self.run_status_and_extract_filenames(status_command, self._status_regexp("U"))
+
+ def added_files(self):
+ return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("A"))
+
@staticmethod
def supports_local_commits():
return True
@@ -420,36 +504,42 @@ class Git(SCM):
return "git"
def create_patch(self):
+ # FIXME: This should probably use cwd=self.checkout_root
return run_command(['git', 'diff', '--binary', 'HEAD'])
@classmethod
def git_commit_from_svn_revision(cls, revision):
+ # FIXME: This should probably use cwd=self.checkout_root
+ git_commit = run_command(['git', 'svn', 'find-rev', 'r%s' % revision]).rstrip()
# git svn find-rev always exits 0, even when the revision is not found.
- return run_command(['git', 'svn', 'find-rev', 'r%s' % revision]).rstrip()
+ if not git_commit:
+ raise ScriptError(message='Failed to find git commit for revision %s, your checkout likely needs an update.' % revision)
+ return git_commit
+
+ def contents_at_revision(self, path, revision):
+ return run_command(["git", "show", "%s:%s" % (self.git_commit_from_svn_revision(revision), path)])
def diff_for_revision(self, revision):
git_commit = self.git_commit_from_svn_revision(revision)
return self.create_patch_from_local_commit(git_commit)
+ def committer_email_for_revision(self, revision):
+ git_commit = self.git_commit_from_svn_revision(revision)
+ committer_email = run_command(["git", "log", "-1", "--pretty=format:%ce", git_commit])
+ # Git adds an extra @repository_hash to the end of every committer email, remove it:
+ return committer_email.rsplit("@", 1)[0]
+
def apply_reverse_diff(self, revision):
# Assume the revision is an svn revision.
git_commit = self.git_commit_from_svn_revision(revision)
- if not git_commit:
- raise ScriptError(message='Failed to find git commit for revision %s, git svn log output: "%s"' % (revision, git_commit))
-
# I think this will always fail due to ChangeLogs.
- # FIXME: We need to detec specific failure conditions and handle them.
run_command(['git', 'revert', '--no-commit', git_commit], error_handler=Executive.ignore_error)
- # Fix any ChangeLogs if necessary.
- changelog_paths = self.modified_changelogs()
- if len(changelog_paths):
- run_command([self.script_path('resolve-ChangeLogs')] + changelog_paths)
-
def revert_files(self, file_paths):
run_command(['git', 'checkout', 'HEAD'] + file_paths)
- def commit_with_message(self, message):
+ def commit_with_message(self, message, username=None):
+ # Username is ignored during Git commits.
self.commit_locally_with_message(message)
return self.push_local_commits_to_server()
@@ -462,6 +552,16 @@ class Git(SCM):
# Git-specific methods:
+ def delete_branch(self, branch):
+ if run_command(['git', 'show-ref', '--quiet', '--verify', 'refs/heads/' + branch], return_exit_code=True) == 0:
+ run_command(['git', 'branch', '-D', branch])
+
+ def svn_merge_base(self):
+ return run_command(['git', 'merge-base', self.svn_branch_name(), 'HEAD']).strip()
+
+ def svn_branch_name(self):
+ return Git.read_git_config('svn-remote.svn.fetch').split(':')[1]
+
def create_patch_from_local_commit(self, commit_id):
return run_command(['git', 'diff', '--binary', commit_id + "^.." + commit_id])
@@ -470,12 +570,16 @@ class Git(SCM):
def commit_locally_with_message(self, message):
run_command(['git', 'commit', '--all', '-F', '-'], input=message)
-
+
def push_local_commits_to_server(self):
+ dcommit_command = ['git', 'svn', 'dcommit']
if self.dryrun:
- # Return a string which looks like a commit so that things which parse this output will succeed.
- return "Dry run, no remote commit.\nCommitted r0"
- return run_command(['git', 'svn', 'dcommit'], error_handler=commit_error_handler)
+ dcommit_command.append('--dry-run')
+ output = run_command(dcommit_command, error_handler=commit_error_handler)
+ # Return a string which looks like a commit so that things which parse this output will succeed.
+ if self.dryrun:
+ output += "\nCommitted r0"
+ return output
# This function supports the following argument formats:
# no args : rev-list trunk..HEAD
@@ -484,8 +588,7 @@ class Git(SCM):
# A B : [A, B] (different from git diff, which would use "rev-list A..B")
def commit_ids_from_commitish_arguments(self, args):
if not len(args):
- # FIXME: trunk is not always the remote branch name, need a way to detect the name.
- args.append('trunk..HEAD')
+ args.append('%s..HEAD' % self.svn_branch_name())
commit_ids = []
for commitish in args:
diff --git a/WebKitTools/Scripts/webkitpy/scm_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py
index 73faf40..c0a64d4 100644
--- a/WebKitTools/Scripts/webkitpy/scm_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py
@@ -28,6 +28,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import base64
+import getpass
import os
import os.path
import re
@@ -38,9 +39,11 @@ import unittest
import urllib
from datetime import date
-from webkitpy.executive import Executive, run_command, ScriptError
-from webkitpy.scm import detect_scm_system, SCM, CheckoutNeedsUpdate, commit_error_handler
-from webkitpy.bugzilla import Attachment # FIXME: This should not be needed
+from webkitpy.common.checkout.api import Checkout
+from webkitpy.common.checkout.scm import detect_scm_system, SCM, SVN, CheckoutNeedsUpdate, commit_error_handler
+from webkitpy.common.config.committers import Committer # FIXME: This should not be needed
+from webkitpy.common.net.bugzilla import Attachment # FIXME: This should not be needed
+from webkitpy.common.system.executive import Executive, run_command, ScriptError
# Eventually we will want to write tests which work for both scms. (like update_webkit, changed_files, etc.)
# Perhaps through some SCMTest base-class which both SVNTest and GitTest inherit from.
@@ -66,31 +69,42 @@ def read_from_path(file_path):
# Exists to share svn repository creation code between the git and svn tests
class SVNTestRepository:
- @staticmethod
- def _setup_test_commits(test_object):
+ @classmethod
+ def _svn_add(cls, path):
+ run_command(["svn", "add", path])
+
+ @classmethod
+ def _svn_commit(cls, message):
+ run_command(["svn", "commit", "--quiet", "--message", message])
+
+ @classmethod
+ def _setup_test_commits(cls, test_object):
# Add some test commits
os.chdir(test_object.svn_checkout_path)
- test_file = open('test_file', 'w')
- test_file.write("test1")
- test_file.flush()
-
- run_command(['svn', 'add', 'test_file'])
- run_command(['svn', 'commit', '--quiet', '--message', 'initial commit'])
-
- test_file.write("test2")
- test_file.flush()
-
- run_command(['svn', 'commit', '--quiet', '--message', 'second commit'])
-
- test_file.write("test3\n")
- test_file.flush()
-
- run_command(['svn', 'commit', '--quiet', '--message', 'third commit'])
-
- test_file.write("test4\n")
- test_file.close()
-
- run_command(['svn', 'commit', '--quiet', '--message', 'fourth commit'])
+
+ write_into_file_at_path("test_file", "test1")
+ cls._svn_add("test_file")
+ cls._svn_commit("initial commit")
+
+ write_into_file_at_path("test_file", "test1test2")
+ # This used to be the last commit, but doing so broke
+ # GitTest.test_apply_git_patch which use the inverse diff of the last commit.
+ # svn-apply fails to remove directories in Git, see:
+ # https://bugs.webkit.org/show_bug.cgi?id=34871
+ os.mkdir("test_dir")
+ # Slash should always be the right path separator since we use cygwin on Windows.
+ test_file3_path = "test_dir/test_file3"
+ write_into_file_at_path(test_file3_path, "third file")
+ cls._svn_add("test_dir")
+ cls._svn_commit("second commit")
+
+ write_into_file_at_path("test_file", "test1test2test3\n")
+ write_into_file_at_path("test_file2", "second file")
+ cls._svn_add("test_file2")
+ cls._svn_commit("third commit")
+
+ write_into_file_at_path("test_file", "test1test2test3\ntest4\n")
+ cls._svn_commit("fourth commit")
# svn does not seem to update after commit as I would expect.
run_command(['svn', 'update'])
@@ -115,6 +129,10 @@ class SVNTestRepository:
run_command(['rm', '-rf', test_object.svn_repo_path])
run_command(['rm', '-rf', test_object.svn_checkout_path])
+ # Now that we've deleted the checkout paths, cwddir may be invalid
+ # Change back to a valid directory so that later calls to os.getcwd() do not fail.
+ os.chdir(detect_scm_system(os.path.dirname(__file__)).checkout_root)
+
# For testing the SCM baseclass directly.
class SCMClassTests(unittest.TestCase):
def setUp(self):
@@ -166,10 +184,14 @@ class SCMTest(unittest.TestCase):
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 Attachment(patch, None) # FIXME: This is a hack, scm.py shouldn't be fetching attachment data.
+
+ attachment = Attachment(patch, None) # FIXME: This is a hack, scm.py shouldn't be fetching attachment data.
+ joe_cool = Committer(name="Joe Cool", email_or_emails=None)
+ attachment._reviewer = joe_cool
+
+ return attachment
def _setup_webkittools_scripts_symlink(self, local_scm):
webkit_scm = detect_scm_system(os.path.dirname(os.path.abspath(__file__)))
@@ -180,16 +202,74 @@ class SCMTest(unittest.TestCase):
# 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):
+ def _shared_test_commit_with_message(self, username="dbates@webkit.org"):
write_into_file_at_path('test_file', 'more test content')
- commit_text = self.scm.commit_with_message('another test commit')
+ commit_text = self.scm.commit_with_message("another test commit", username)
self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '5')
self.scm.dryrun = True
write_into_file_at_path('test_file', 'still more test content')
- commit_text = self.scm.commit_with_message('yet another test commit')
+ commit_text = self.scm.commit_with_message("yet another test commit", username)
self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0')
+ def _shared_test_changed_files(self):
+ write_into_file_at_path("test_file", "changed content")
+ self.assertEqual(self.scm.changed_files(), ["test_file"])
+ write_into_file_at_path("test_dir/test_file3", "new stuff")
+ self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
+ old_cwd = os.getcwd()
+ os.chdir("test_dir")
+ # Validate that changed_files does not change with our cwd, see bug 37015.
+ self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
+ os.chdir(old_cwd)
+
+ def _shared_test_added_files(self):
+ write_into_file_at_path("test_file", "changed content")
+ self.assertEqual(self.scm.added_files(), [])
+
+ write_into_file_at_path("added_file", "new stuff")
+ self.scm.add("added_file")
+
+ os.mkdir("added_dir")
+ write_into_file_at_path("added_dir/added_file2", "new stuff")
+ self.scm.add("added_dir")
+
+ # SVN reports directory changes, Git does not.
+ added_files = self.scm.added_files()
+ if "added_dir" in added_files:
+ added_files.remove("added_dir")
+ self.assertEqual(added_files, ["added_dir/added_file2", "added_file"])
+
+ # Test also to make sure clean_working_directory removes added files
+ self.scm.clean_working_directory()
+ self.assertEqual(self.scm.added_files(), [])
+ self.assertFalse(os.path.exists("added_file"))
+ self.assertFalse(os.path.exists("added_dir"))
+
+ def _shared_test_changed_files_for_revision(self):
+ # SVN reports directory changes, Git does not.
+ changed_files = self.scm.changed_files_for_revision(2)
+ if "test_dir" in changed_files:
+ changed_files.remove("test_dir")
+ self.assertEqual(changed_files, ["test_dir/test_file3", "test_file"])
+ self.assertEqual(sorted(self.scm.changed_files_for_revision(3)), sorted(["test_file", "test_file2"])) # Git and SVN return different orders.
+ self.assertEqual(self.scm.changed_files_for_revision(4), ["test_file"])
+
+ def _shared_test_contents_at_revision(self):
+ self.assertEqual(self.scm.contents_at_revision("test_file", 2), "test1test2")
+ self.assertEqual(self.scm.contents_at_revision("test_file", 3), "test1test2test3\n")
+ self.assertEqual(self.scm.contents_at_revision("test_file", 4), "test1test2test3\ntest4\n")
+
+ self.assertEqual(self.scm.contents_at_revision("test_file2", 3), "second file")
+ # Files which don't exist:
+ # Currently we raise instead of returning None because detecting the difference between
+ # "file not found" and any other error seems impossible with svn (git seems to expose such through the return code).
+ self.assertRaises(ScriptError, self.scm.contents_at_revision, "test_file2", 2)
+ self.assertRaises(ScriptError, self.scm.contents_at_revision, "does_not_exist", 2)
+
+ def _shared_test_committer_email_for_revision(self):
+ self.assertEqual(self.scm.committer_email_for_revision(2), getpass.getuser()) # Committer "email" will be the current user
+
def _shared_test_reverse_diff(self):
self._setup_webkittools_scripts_symlink(self.scm) # Git's apply_reverse_diff uses resolve-ChangeLogs
# Only test the simple case, as any other will end up with conflict markers.
@@ -227,14 +307,14 @@ literal 0
HcmV?d00001
"""
- self.scm.apply_patch(self._create_patch(git_binary_addition))
+ self.checkout.apply_patch(self._create_patch(git_binary_addition))
added = read_from_path('fizzbuzz7.gif')
self.assertEqual(512, len(added))
self.assertTrue(added.startswith('GIF89a'))
self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
# The file already exists.
- self.assertRaises(ScriptError, self.scm.apply_patch, self._create_patch(git_binary_addition))
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_addition))
git_binary_modification = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
index 64a9532e7794fcd791f6f12157406d9060151690..323fae03f4606ea9991df8befbb2fca7
@@ -255,13 +335,13 @@ z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4
ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H
"""
- self.scm.apply_patch(self._create_patch(git_binary_modification))
+ self.checkout.apply_patch(self._create_patch(git_binary_modification))
modified = read_from_path('fizzbuzz7.gif')
self.assertEqual('foobar\n', modified)
self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
# Applying the same modification should fail.
- self.assertRaises(ScriptError, self.scm.apply_patch, self._create_patch(git_binary_modification))
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_modification))
git_binary_deletion = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
deleted file mode 100644
@@ -274,12 +354,12 @@ literal 7
OcmYex&reD$;sO8*F9L)B
"""
- self.scm.apply_patch(self._create_patch(git_binary_deletion))
+ self.checkout.apply_patch(self._create_patch(git_binary_deletion))
self.assertFalse(os.path.exists('fizzbuzz7.gif'))
self.assertFalse('fizzbuzz7.gif' in self.scm.changed_files())
# Cannot delete again.
- self.assertRaises(ScriptError, self.scm.apply_patch, self._create_patch(git_binary_deletion))
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_deletion))
class SVNTest(SCMTest):
@@ -314,24 +394,24 @@ class SVNTest(SCMTest):
+++ ChangeLog (working copy)
@@ -1,5 +1,13 @@
2009-10-26 Eric Seidel <eric@webkit.org>
-
+
+ Reviewed by NOBODY (OOPS!).
+
-+ Second most awsome change ever.
++ Second most awesome change ever.
+
+ * scm_unittest.py:
+
+2009-10-26 Eric Seidel <eric@webkit.org>
+
Reviewed by Foo Bar.
-
+
Most awesome change ever.
"""
one_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
Reviewed by REVIEWER_HERE.
- Second most awsome change ever.
+ Second most awesome change ever.
* scm_unittest.py:
"""
@@ -340,10 +420,10 @@ class SVNTest(SCMTest):
--- ChangeLog (revision 5)
+++ ChangeLog (working copy)
@@ -2,6 +2,14 @@
-
+
Reviewed by Foo Bar.
-
-+ Second most awsome change ever.
+
++ Second most awesome change ever.
+
+ * scm_unittest.py:
+
@@ -352,14 +432,14 @@ class SVNTest(SCMTest):
+ Reviewed by Foo Bar.
+
Most awesome change ever.
-
+
* scm_unittest.py:
"""
two_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
Reviewed by Foo Bar.
- Second most awsome change ever.
+ Second most awesome change ever.
* scm_unittest.py:
"""
@@ -374,12 +454,12 @@ class SVNTest(SCMTest):
run_command(['svn', 'commit', '--quiet', '--message', 'Intermediate commit'])
self._setup_webkittools_scripts_symlink(self.scm)
- self.scm.apply_patch(self._create_patch(one_line_overlap_patch))
+ self.checkout.apply_patch(self._create_patch(one_line_overlap_patch))
expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(one_line_overlap_entry), changelog_contents)
self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
self.scm.revert_files(['ChangeLog'])
- self.scm.apply_patch(self._create_patch(two_line_overlap_patch))
+ self.checkout.apply_patch(self._create_patch(two_line_overlap_patch))
expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(two_line_overlap_entry), changelog_contents)
self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
@@ -387,16 +467,18 @@ class SVNTest(SCMTest):
SVNTestRepository.setup(self)
os.chdir(self.svn_checkout_path)
self.scm = detect_scm_system(self.svn_checkout_path)
+ # For historical reasons, we test some checkout code here too.
+ self.checkout = Checkout(self.scm)
def tearDown(self):
SVNTestRepository.tear_down(self)
def test_create_patch_is_full_patch(self):
- test_dir_path = os.path.join(self.svn_checkout_path, 'test_dir')
+ test_dir_path = os.path.join(self.svn_checkout_path, "test_dir2")
os.mkdir(test_dir_path)
test_file_path = os.path.join(test_dir_path, 'test_file2')
write_into_file_at_path(test_file_path, 'test content')
- run_command(['svn', 'add', 'test_dir'])
+ run_command(['svn', 'add', 'test_dir2'])
# create_patch depends on 'svn-create-patch', so make a dummy version.
scripts_path = os.path.join(self.svn_checkout_path, 'WebKitTools', 'Scripts')
@@ -435,7 +517,7 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
expected_contents = base64.b64decode("Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==")
self._setup_webkittools_scripts_symlink(self.scm)
patch_file = self._create_patch(patch_contents)
- self.scm.apply_patch(patch_file)
+ self.checkout.apply_patch(patch_file)
actual_contents = read_from_path("test_file.swf")
self.assertEqual(actual_contents, expected_contents)
@@ -443,13 +525,13 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
scm = detect_scm_system(self.svn_checkout_path)
patch = self._create_patch(run_command(['svn', 'diff', '-r4:3']))
self._setup_webkittools_scripts_symlink(scm)
- scm.apply_patch(patch)
+ Checkout(scm).apply_patch(patch)
def test_apply_svn_patch_force(self):
scm = detect_scm_system(self.svn_checkout_path)
patch = self._create_patch(run_command(['svn', 'diff', '-r2:4']))
self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, scm.apply_patch, patch, force=True)
+ self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
def test_commit_logs(self):
# Commits have dates and usernames in them, so we can't just direct compare.
@@ -459,6 +541,30 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
def test_commit_text_parsing(self):
self._shared_test_commit_with_message()
+ def test_commit_with_username(self):
+ self._shared_test_commit_with_message("dbates@webkit.org")
+
+ def test_has_authorization_for_realm(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
+ svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
+ os.mkdir(svn_config_dir_path)
+ fake_webkit_auth_file = os.path.join(svn_config_dir_path, "fake_webkit_auth_file")
+ write_into_file_at_path(fake_webkit_auth_file, SVN.svn_server_realm)
+ self.assertTrue(scm.has_authorization_for_realm(home_directory=fake_home_dir))
+ os.remove(fake_webkit_auth_file)
+ os.rmdir(svn_config_dir_path)
+ os.rmdir(fake_home_dir)
+
+ def test_not_have_authorization_for_realm(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
+ svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
+ os.mkdir(svn_config_dir_path)
+ self.assertFalse(scm.has_authorization_for_realm(home_directory=fake_home_dir))
+ os.rmdir(svn_config_dir_path)
+ os.rmdir(fake_home_dir)
+
def test_reverse_diff(self):
self._shared_test_reverse_diff()
@@ -468,6 +574,22 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
def test_svn_apply_git_patch(self):
self._shared_test_svn_apply_git_patch()
+ def test_changed_files(self):
+ self._shared_test_changed_files()
+
+ def test_changed_files_for_revision(self):
+ self._shared_test_changed_files_for_revision()
+
+ def test_added_files(self):
+ self._shared_test_added_files()
+
+ def test_contents_at_revision(self):
+ self._shared_test_contents_at_revision()
+
+ def test_committer_email_for_revision(self):
+ self._shared_test_committer_email_for_revision()
+
+
class GitTest(SCMTest):
def _setup_git_clone_of_svn_repository(self):
@@ -483,6 +605,8 @@ class GitTest(SCMTest):
self._setup_git_clone_of_svn_repository()
os.chdir(self.git_checkout_path)
self.scm = detect_scm_system(self.git_checkout_path)
+ # For historical reasons, we test some checkout code here too.
+ self.checkout = Checkout(self.scm)
def tearDown(self):
SVNTestRepository.tear_down(self)
@@ -493,6 +617,52 @@ class GitTest(SCMTest):
self.assertEqual(scm.display_name(), "git")
self.assertEqual(scm.supports_local_commits(), True)
+ def test_read_git_config(self):
+ key = 'test.git-config'
+ value = 'git-config value'
+ run_command(['git', 'config', key, value])
+ self.assertEqual(self.scm.read_git_config(key), value)
+
+ def test_local_commits(self):
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+ run_command(['git', 'commit', '-a', '-m', 'local commit'])
+
+ self.assertEqual(len(self.scm.local_commits()), 1)
+
+ def test_discard_local_commits(self):
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+ run_command(['git', 'commit', '-a', '-m', 'local commit'])
+
+ self.assertEqual(len(self.scm.local_commits()), 1)
+ self.scm.discard_local_commits()
+ self.assertEqual(len(self.scm.local_commits()), 0)
+
+ def test_delete_branch(self):
+ old_branch = run_command(['git', 'symbolic-ref', 'HEAD']).strip()
+ new_branch = 'foo'
+
+ run_command(['git', 'checkout', '-b', new_branch])
+ self.assertEqual(run_command(['git', 'symbolic-ref', 'HEAD']).strip(), 'refs/heads/' + new_branch)
+
+ run_command(['git', 'checkout', old_branch])
+ self.scm.delete_branch(new_branch)
+
+ self.assertFalse(re.search(r'foo', run_command(['git', 'branch'])))
+
+ def test_svn_merge_base(self):
+ # Diff to merge-base should include working-copy changes,
+ # which the diff to svn_branch.. doesn't.
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+
+ diff_to_common_base = run_command(['git', 'diff', self.scm.svn_branch_name() + '..'])
+ diff_to_merge_base = run_command(['git', 'diff', self.scm.svn_merge_base()])
+
+ self.assertFalse(re.search(r'foo', diff_to_common_base))
+ self.assertTrue(re.search(r'foo', diff_to_merge_base))
+
def test_rebase_in_progress(self):
svn_test_file = os.path.join(self.svn_checkout_path, 'test_file')
write_into_file_at_path(svn_test_file, "svn_checkout")
@@ -538,15 +708,18 @@ class GitTest(SCMTest):
def test_apply_git_patch(self):
scm = detect_scm_system(self.git_checkout_path)
+ # We carefullly pick a diff which does not have a directory addition
+ # as currently svn-apply will error out when trying to remove directories
+ # in Git: https://bugs.webkit.org/show_bug.cgi?id=34871
patch = self._create_patch(run_command(['git', 'diff', 'HEAD..HEAD^']))
self._setup_webkittools_scripts_symlink(scm)
- scm.apply_patch(patch)
+ Checkout(scm).apply_patch(patch)
def test_apply_git_patch_force(self):
scm = detect_scm_system(self.git_checkout_path)
patch = self._create_patch(run_command(['git', 'diff', 'HEAD~2..HEAD']))
self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, scm.apply_patch, patch, force=True)
+ self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
def test_commit_text_parsing(self):
self._shared_test_commit_with_message()
@@ -575,7 +748,7 @@ class GitTest(SCMTest):
# Check if we can apply the created patch.
run_command(['git', 'rm', '-f', test_file_name])
self._setup_webkittools_scripts_symlink(scm)
- self.scm.apply_patch(self._create_patch(patch))
+ self.checkout.apply_patch(self._create_patch(patch))
self.assertEqual(file_contents, read_from_path(test_file_path))
# Check if we can create a patch from a local commit.
@@ -590,6 +763,21 @@ class GitTest(SCMTest):
self.assertTrue(re.search(r'\nliteral 256\n', patch_since_local_commit))
self.assertEqual(patch_from_local_commit, patch_since_local_commit)
+ def test_changed_files(self):
+ self._shared_test_changed_files()
+
+ def test_changed_files_for_revision(self):
+ self._shared_test_changed_files_for_revision()
+
+ def test_contents_at_revision(self):
+ self._shared_test_contents_at_revision()
+
+ def test_added_files(self):
+ self._shared_test_added_files()
+
+ def test_committer_email_for_revision(self):
+ self._shared_test_committer_email_for_revision()
+
if __name__ == '__main__':
unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/config/__init__.py b/WebKitTools/Scripts/webkitpy/common/config/__init__.py
new file mode 100644
index 0000000..03f1bc7
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/config/__init__.py
@@ -0,0 +1,7 @@
+# Required for Python to search this directory for module files
+
+import re
+
+codereview_server_host = "wkrietveld.appspot.com"
+codereview_server_regex = "https?://%s/" % re.sub('\.', '\\.', codereview_server_host)
+codereview_server_url = "https://%s/" % codereview_server_host
diff --git a/WebKitTools/Scripts/webkitpy/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py
index 6413243..a92dbd3 100644
--- a/WebKitTools/Scripts/webkitpy/committers.py
+++ b/WebKitTools/Scripts/webkitpy/common/config/committers.py
@@ -31,12 +31,13 @@
class Committer:
- def __init__(self, name, email_or_emails):
+ def __init__(self, name, email_or_emails, irc_nickname=None):
self.full_name = name
if isinstance(email_or_emails, str):
self.emails = [email_or_emails]
else:
self.emails = email_or_emails
+ self.irc_nickname = irc_nickname
self.can_review = False
def bugzilla_email(self):
@@ -50,8 +51,8 @@ class Committer:
class Reviewer(Committer):
- def __init__(self, name, email_or_emails):
- Committer.__init__(self, name, email_or_emails)
+ def __init__(self, name, email_or_emails, irc_nickname=None):
+ Committer.__init__(self, name, email_or_emails, irc_nickname)
self.can_review = True
@@ -62,103 +63,110 @@ class Reviewer(Committer):
committers_unable_to_review = [
- Committer("Aaron Boodman", "aa@chromium.org"),
- Committer("Adam Langley", "agl@chromium.org"),
+ Committer("Aaron Boodman", "aa@chromium.org", "aboodman"),
+ Committer("Adam Langley", "agl@chromium.org", "agl"),
Committer("Albert J. Wong", "ajwong@chromium.org"),
Committer("Alejandro G. Castro", ["alex@igalia.com", "alex@webkit.org"]),
- Committer("Alexander Kellett", ["lypanov@mac.com", "a-lists001@lypanov.net", "lypanov@kde.org"]),
+ Committer("Alexander Kellett", ["lypanov@mac.com", "a-lists001@lypanov.net", "lypanov@kde.org"], "lypanov"),
Committer("Alexander Pavlov", "apavlov@chromium.org"),
Committer("Andre Boule", "aboule@apple.com"),
- Committer("Andrew Wellington", ["andrew@webkit.org", "proton@wiretapped.net"]),
- Committer("Andras Becsi", "abecsi@webkit.org"),
- Committer("Anthony Ricaud", "rik@webkit.org"),
- Committer("Anton Muhin", "antonm@chromium.org"),
- Committer("Antonio Gomes", "tonikitoo@webkit.org"),
- Committer("Ben Murdoch", "benm@google.com"),
- Committer("Benjamin C Meyer", ["ben@meyerhome.net", "ben@webkit.org"]),
- Committer("Benjamin Otte", ["otte@gnome.org", "otte@webkit.org"]),
- Committer("Brent Fulgham", "bfulgham@webkit.org"),
- Committer("Brett Wilson", "brettw@chromium.org"),
- Committer("Brian Weinstein", "bweinstein@apple.com"),
- Committer("Cameron McCormack", "cam@webkit.org"),
+ Committer("Andrew Wellington", ["andrew@webkit.org", "proton@wiretapped.net"], "proton"),
+ Committer("Andras Becsi", "abecsi@webkit.org", "bbandix"),
+ Committer("Andy Estes", "aestes@apple.com", "estes"),
+ Committer("Anthony Ricaud", "rik@webkit.org", "rik"),
+ Committer("Anton Muhin", "antonm@chromium.org", "antonm"),
+ Committer("Antonio Gomes", "tonikitoo@webkit.org", "tonikitoo"),
+ Committer("Ben Murdoch", "benm@google.com", "benm"),
+ Committer("Benjamin C Meyer", ["ben@meyerhome.net", "ben@webkit.org"], "icefox"),
+ Committer("Benjamin Otte", ["otte@gnome.org", "otte@webkit.org"], "otte"),
+ Committer("Brent Fulgham", "bfulgham@webkit.org", "bfulgham"),
+ Committer("Brett Wilson", "brettw@chromium.org", "brettx"),
+ Committer("Brian Weinstein", "bweinstein@apple.com", "bweinstein"),
+ Committer("Cameron McCormack", "cam@webkit.org", "heycam"),
Committer("Carol Szabo", "carol.szabo@nokia.com"),
Committer("Chang Shu", "Chang.Shu@nokia.com"),
Committer("Chris Fleizach", "cfleizach@apple.com"),
- Committer("Chris Jerdonek", "cjerdonek@webkit.org"),
- Committer("Chris Marrin", "cmarrin@apple.com"),
- Committer("Chris Petersen", "cpetersen@apple.com"),
+ Committer("Chris Jerdonek", "cjerdonek@webkit.org", "cjerdonek"),
+ Committer("Chris Marrin", "cmarrin@apple.com", "cmarrin"),
+ Committer("Chris Petersen", "cpetersen@apple.com", "cpetersen"),
Committer("Christian Dywan", ["christian@twotoasts.de", "christian@webkit.org"]),
Committer("Collin Jackson", "collinj@webkit.org"),
- Committer("Csaba Osztrogonac", "ossy@webkit.org"),
- Committer("Daniel Bates", "dbates@webkit.org"),
- Committer("David Smith", ["catfish.man@gmail.com", "dsmith@webkit.org"]),
- Committer("Dean Jackson", "dino@apple.com"),
+ Committer("Csaba Osztrogonac", "ossy@webkit.org", "ossy"),
+ Committer("David Smith", ["catfish.man@gmail.com", "dsmith@webkit.org"], "catfishman"),
+ Committer("Dean Jackson", "dino@apple.com", "dino"),
Committer("Dirk Pranke", "dpranke@chromium.org"),
- Committer("Drew Wilson", "atwilson@chromium.org"),
- Committer("Dumitru Daniliuc", "dumi@chromium.org"),
- Committer("Eli Fidler", "eli@staikos.net"),
+ Committer("Drew Wilson", "atwilson@chromium.org", "atwilson"),
+ Committer("Dumitru Daniliuc", "dumi@chromium.org", "dumi"),
+ Committer("Eli Fidler", "eli@staikos.net", "QBin"),
Committer("Enrica Casucci", "enrica@apple.com"),
- Committer("Erik Arvidsson", "arv@chromium.org"),
- Committer("Eric Roman", "eroman@chromium.org"),
+ Committer("Erik Arvidsson", "arv@chromium.org", "arv"),
+ Committer("Eric Roman", "eroman@chromium.org", "eroman"),
Committer("Feng Qian", "feng@chromium.org"),
- Committer("Fumitoshi Ukai", "ukai@chromium.org"),
- Committer("Gabor Loki", "loki@webkit.org"),
+ Committer("Fumitoshi Ukai", "ukai@chromium.org", "ukai"),
+ Committer("Gabor Loki", "loki@webkit.org", "loki04"),
Committer("Girish Ramakrishnan", ["girish@forwardbias.in", "ramakrishnan.girish@gmail.com"]),
Committer("Graham Dennis", ["Graham.Dennis@gmail.com", "gdennis@webkit.org"]),
Committer("Greg Bolsinga", "bolsinga@apple.com"),
Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]),
- Committer("Jakob Petsovits", ["jpetsovits@rim.com", "jpetso@gmx.at"]),
+ Committer("Ilya Tikhonovsky", "loislo@chromium.org", "loislo"),
+ Committer("Jakob Petsovits", ["jpetsovits@rim.com", "jpetso@gmx.at"], "jpetso"),
+ Committer("Jakub Wieczorek", "jwieczorek@webkit.org", "fawek"),
+ Committer("James Hawkins", ["jhawkins@chromium.org", "jhawkins@google.com"], "jhawkins"),
+ Committer("James Robinson", ["jamesr@chromium.org", "jamesr@google.com"]),
Committer("Jens Alfke", ["snej@chromium.org", "jens@apple.com"]),
- Committer("Jeremy Moskovich", ["playmobil@google.com", "jeremy@chromium.org"]),
+ Committer("Jeremy Moskovich", ["playmobil@google.com", "jeremy@chromium.org"], "jeremymos"),
Committer("Jessie Berlin", ["jberlin@webkit.org", "jberlin@apple.com"]),
- Committer("Jian Li", "jianli@chromium.org"),
- Committer("John Abd-El-Malek", "jam@chromium.org"),
- Committer("Joost de Valk", ["joost@webkit.org", "webkit-dev@joostdevalk.nl"]),
- Committer("Joseph Pecoraro", "joepeck@webkit.org"),
- Committer("Julie Parent", ["jparent@google.com", "jparent@chromium.org"]),
+ Committer("Jesus Sanchez-Palencia", ["jesus@webkit.org", "jesus.palencia@openbossa.org"], "jeez_"),
+ Committer("John Abd-El-Malek", "jam@chromium.org", "jam"),
+ Committer("John Gregg", ["johnnyg@google.com", "johnnyg@chromium.org"], "johnnyg"),
+ Committer("Joost de Valk", ["joost@webkit.org", "webkit-dev@joostdevalk.nl"], "Altha"),
+ Committer("Julie Parent", ["jparent@google.com", "jparent@chromium.org"], "jparent"),
Committer("Julien Chaffraix", ["jchaffraix@webkit.org", "julien.chaffraix@gmail.com"]),
Committer("Jungshik Shin", "jshin@chromium.org"),
- Committer("Keishi Hattori", "keishi@webkit.org"),
+ Committer("Keishi Hattori", "keishi@webkit.org", "keishi"),
Committer("Kelly Norton", "knorton@google.com"),
Committer("Kenneth Russell", "kbr@google.com"),
- Committer("Kent Tamura", "tkent@chromium.org"),
+ Committer("Kent Tamura", "tkent@chromium.org", "tkent"),
+ Committer("Kinuko Yasuda", "kinuko@chromium.org", "kinuko"),
Committer("Krzysztof Kowalczyk", "kkowalczyk@gmail.com"),
Committer("Levi Weintraub", "lweintraub@apple.com"),
Committer("Mads Ager", "ager@chromium.org"),
Committer("Matt Lilek", ["webkit@mattlilek.com", "pewtermoose@webkit.org"]),
Committer("Matt Perry", "mpcomplete@chromium.org"),
Committer("Maxime Britto", ["maxime.britto@gmail.com", "britto@apple.com"]),
- Committer("Maxime Simon", ["simon.maxime@gmail.com", "maxime.simon@webkit.org"]),
+ Committer("Maxime Simon", ["simon.maxime@gmail.com", "maxime.simon@webkit.org"], "maxime.simon"),
Committer("Martin Robinson", ["mrobinson@webkit.org", "martin.james.robinson@gmail.com"]),
- Committer("Michelangelo De Simone", "michelangelo@webkit.org"),
+ Committer("Michelangelo De Simone", "michelangelo@webkit.org", "michelangelo"),
Committer("Mike Belshe", ["mbelshe@chromium.org", "mike@belshe.com"]),
- Committer("Mike Fenton", ["mike.fenton@torchmobile.com", "mifenton@rim.com"]),
+ Committer("Mike Fenton", ["mike.fenton@torchmobile.com", "mifenton@rim.com"], "mfenton"),
Committer("Mike Thole", ["mthole@mikethole.com", "mthole@apple.com"]),
Committer("Mikhail Naganov", "mnaganov@chromium.org"),
- Committer("Ojan Vafai", "ojan@chromium.org"),
- Committer("Pam Greene", "pam@chromium.org"),
- Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"]),
- Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"]),
- Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"]),
- Committer("Pierre-Olivier Latour", "pol@apple.com"),
+ Committer("MORITA Hajime", "morrita@google.com", "morrita"),
+ Committer("Ojan Vafai", "ojan@chromium.org", "ojan"),
+ Committer("Pam Greene", "pam@chromium.org", "pamg"),
+ Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"], "pkasting"),
+ Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "pnormand"),
+ Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"], "pdherbemont"),
+ Committer("Pierre-Olivier Latour", "pol@apple.com", "pol"),
+ Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net"], "mwenge"),
Committer("Roland Steiner", "rolandsteiner@chromium.org"),
- Committer("Ryosuke Niwa", "rniwa@webkit.org"),
- Committer("Scott Violet", "sky@chromium.org"),
- Committer("Stephen White", "senorblanco@chromium.org"),
+ Committer("Ryosuke Niwa", "rniwa@webkit.org", "rniwa"),
+ Committer("Scott Violet", "sky@chromium.org", "sky"),
+ Committer("Stephen White", "senorblanco@chromium.org", "senorblanco"),
Committer("Steve Block", "steveblock@google.com"),
- Committer("Tony Chang", "tony@chromium.org"),
- Committer("Trey Matteson", "trey@usa.net"),
+ Committer("Tony Chang", "tony@chromium.org", "tony^work"),
+ Committer("Trey Matteson", "trey@usa.net", "trey"),
Committer("Tristan O'Tierney", ["tristan@otierney.net", "tristan@apple.com"]),
Committer("Victor Wang", "victorw@chromium.org"),
- Committer("William Siegrist", "wsiegrist@apple.com"),
+ Committer("Vitaly Repeshko", "vitalyr@chromium.org"),
+ Committer("William Siegrist", "wsiegrist@apple.com", "wms"),
Committer("Yael Aharon", "yael.aharon@nokia.com"),
Committer("Yaar Schnitman", ["yaar@chromium.org", "yaar@google.com"]),
- Committer("Yong Li", ["yong.li@torchmobile.com", "yong.li.webkit@gmail.com"]),
+ Committer("Yong Li", ["yong.li@torchmobile.com", "yong.li.webkit@gmail.com"], "yong"),
Committer("Yongjun Zhang", "yongjun.zhang@nokia.com"),
- Committer("Yury Semikhatsky", "yurys@chromium.org"),
- Committer("Yuzo Fujishima", "yuzo@google.com"),
- Committer("Zoltan Herczeg", "zherczeg@webkit.org"),
- Committer("Zoltan Horvath", "zoltan@webkit.org"),
+ Committer("Yuzo Fujishima", "yuzo@google.com", "yuzo"),
+ Committer("Zoltan Herczeg", "zherczeg@webkit.org", "zherczeg"),
+ Committer("Zoltan Horvath", "zoltan@webkit.org", "zoltan"),
]
@@ -168,71 +176,75 @@ committers_unable_to_review = [
reviewers_list = [
- Reviewer("Ada Chan", "adachan@apple.com"),
- Reviewer("Adam Barth", "abarth@webkit.org"),
- Reviewer("Adam Roben", "aroben@apple.com"),
- Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org"]),
- Reviewer("Adele Peterson", "adele@apple.com"),
- Reviewer("Alexey Proskuryakov", ["ap@webkit.org", "ap@apple.com"]),
- Reviewer("Alice Liu", "alice.liu@apple.com"),
- Reviewer("Alp Toker", ["alp@nuanti.com", "alp@atoker.com", "alp@webkit.org"]),
- Reviewer("Anders Carlsson", ["andersca@apple.com", "acarlsson@apple.com"]),
- Reviewer("Antti Koivisto", ["koivisto@iki.fi", "antti@apple.com"]),
- Reviewer("Ariya Hidayat", ["ariya.hidayat@gmail.com", "ariya@webkit.org"]),
- Reviewer("Beth Dakin", "bdakin@apple.com"),
- Reviewer("Brady Eidson", "beidson@apple.com"),
+ Reviewer("Ada Chan", "adachan@apple.com", "chanada"),
+ Reviewer("Adam Barth", "abarth@webkit.org", "abarth"),
+ Reviewer("Adam Roben", "aroben@apple.com", "aroben"),
+ Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org"], "manyoso"),
+ Reviewer("Adele Peterson", "adele@apple.com", "adele"),
+ Reviewer("Alexey Proskuryakov", ["ap@webkit.org", "ap@apple.com"], "ap"),
+ Reviewer("Alice Liu", "alice.liu@apple.com", "aliu"),
+ Reviewer("Alp Toker", ["alp@nuanti.com", "alp@atoker.com", "alp@webkit.org"], "alp"),
+ Reviewer("Anders Carlsson", ["andersca@apple.com", "acarlsson@apple.com"], "andersca"),
+ Reviewer("Antti Koivisto", ["koivisto@iki.fi", "antti@apple.com"], "anttik"),
+ Reviewer("Ariya Hidayat", ["ariya.hidayat@gmail.com", "ariya@webkit.org"], "ariya"),
+ Reviewer("Beth Dakin", "bdakin@apple.com", "dethbakin"),
+ Reviewer("Brady Eidson", "beidson@apple.com", "bradee-oh"),
Reviewer("Cameron Zwarich", ["zwarich@apple.com", "cwzwarich@apple.com", "cwzwarich@webkit.org"]),
- Reviewer("Chris Blumenberg", "cblu@apple.com"),
- Reviewer("Dan Bernstein", ["mitz@webkit.org", "mitz@apple.com"]),
- Reviewer("Darin Adler", "darin@apple.com"),
- Reviewer("Darin Fisher", ["fishd@chromium.org", "darin@chromium.org"]),
- Reviewer("David Harrison", "harrison@apple.com"),
- Reviewer("David Hyatt", "hyatt@apple.com"),
- Reviewer("David Kilzer", ["ddkilzer@webkit.org", "ddkilzer@apple.com"]),
- Reviewer("David Levin", "levin@chromium.org"),
- Reviewer("Dimitri Glazkov", "dglazkov@chromium.org"),
- Reviewer("Dirk Schulze", "krit@webkit.org"),
- Reviewer("Dmitry Titov", "dimich@chromium.org"),
- Reviewer("Don Melton", "gramps@apple.com"),
+ Reviewer("Chris Blumenberg", "cblu@apple.com", "cblu"),
+ Reviewer("Dan Bernstein", ["mitz@webkit.org", "mitz@apple.com"], "mitzpettel"),
+ Reviewer("Daniel Bates", "dbates@webkit.org", "dydz"),
+ Reviewer("Darin Adler", "darin@apple.com", "darin"),
+ Reviewer("Darin Fisher", ["fishd@chromium.org", "darin@chromium.org"], "fishd"),
+ Reviewer("David Harrison", "harrison@apple.com", "harrison"),
+ Reviewer("David Hyatt", "hyatt@apple.com", "hyatt"),
+ Reviewer("David Kilzer", ["ddkilzer@webkit.org", "ddkilzer@apple.com"], "ddkilzer"),
+ Reviewer("David Levin", "levin@chromium.org", "dave_levin"),
+ Reviewer("Dimitri Glazkov", "dglazkov@chromium.org", "dglazkov"),
+ Reviewer("Dirk Schulze", "krit@webkit.org", "krit"),
+ Reviewer("Dmitry Titov", "dimich@chromium.org", "dimich"),
+ Reviewer("Don Melton", "gramps@apple.com", "gramps"),
Reviewer("Eric Carlson", "eric.carlson@apple.com"),
- Reviewer("Eric Seidel", "eric@webkit.org"),
- Reviewer("Gavin Barraclough", "barraclough@apple.com"),
- Reviewer("Geoffrey Garen", "ggaren@apple.com"),
+ Reviewer("Eric Seidel", "eric@webkit.org", "eseidel"),
+ Reviewer("Gavin Barraclough", "barraclough@apple.com", "gbarra"),
+ Reviewer("Geoffrey Garen", "ggaren@apple.com", "ggaren"),
Reviewer("George Staikos", ["staikos@kde.org", "staikos@webkit.org"]),
- Reviewer("Gustavo Noronha Silva", ["gns@gnome.org", "kov@webkit.org"]),
- Reviewer("Holger Freyther", ["zecke@selfish.org", "zecke@webkit.org"]),
- Reviewer("Jan Alonzo", ["jmalonzo@gmail.com", "jmalonzo@webkit.org"]),
- Reviewer("Jeremy Orlow", "jorlow@chromium.org"),
- Reviewer("John Sullivan", "sullivan@apple.com"),
- Reviewer("Jon Honeycutt", "jhoneycutt@apple.com"),
- Reviewer("Justin Garcia", "justin.garcia@apple.com"),
+ Reviewer("Gustavo Noronha Silva", ["gns@gnome.org", "kov@webkit.org"], "kov"),
+ Reviewer("Holger Freyther", ["zecke@selfish.org", "zecke@webkit.org"], "zecke"),
+ Reviewer("Jan Alonzo", ["jmalonzo@gmail.com", "jmalonzo@webkit.org"], "janm"),
+ Reviewer("Jeremy Orlow", "jorlow@chromium.org", "jorlow"),
+ Reviewer("Jian Li", "jianli@chromium.org", "jianli"),
+ Reviewer("John Sullivan", "sullivan@apple.com", "sullivan"),
+ Reviewer("Jon Honeycutt", "jhoneycutt@apple.com", "jhoneycutt"),
+ Reviewer("Joseph Pecoraro", "joepeck@webkit.org", "JoePeck"),
+ Reviewer("Justin Garcia", "justin.garcia@apple.com", "justing"),
Reviewer("Ken Kocienda", "kocienda@apple.com"),
- Reviewer("Kenneth Rohde Christiansen", ["kenneth@webkit.org", "kenneth.christiansen@openbossa.org"]),
- Reviewer("Kevin Decker", "kdecker@apple.com"),
- Reviewer("Kevin McCullough", "kmccullough@apple.com"),
- Reviewer("Kevin Ollivier", ["kevino@theolliviers.com", "kevino@webkit.org"]),
- Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org"]),
- Reviewer("Laszlo Gombos", "laszlo.1.gombos@nokia.com"),
- Reviewer("Maciej Stachowiak", "mjs@apple.com"),
- Reviewer("Mark Rowe", "mrowe@apple.com"),
- Reviewer("Nate Chapin", "japhet@chromium.org"),
- Reviewer("Nikolas Zimmermann", ["zimmermann@kde.org", "zimmermann@physik.rwth-aachen.de", "zimmermann@webkit.org"]),
- Reviewer("Oliver Hunt", "oliver@apple.com"),
- Reviewer("Pavel Feldman", "pfeldman@chromium.org"),
- Reviewer("Richard Williamson", "rjw@apple.com"),
- Reviewer("Rob Buis", ["rwlbuis@gmail.com", "rwlbuis@webkit.org"]),
- Reviewer("Sam Weinig", ["sam@webkit.org", "weinig@apple.com"]),
- Reviewer("Shinichiro Hamaji", "hamaji@chromium.org"),
- Reviewer("Simon Fraser", "simon.fraser@apple.com"),
- Reviewer("Simon Hausmann", ["hausmann@webkit.org", "hausmann@kde.org", "simon.hausmann@nokia.com"]),
- Reviewer("Stephanie Lewis", "slewis@apple.com"),
- Reviewer("Steve Falkenburg", "sfalken@apple.com"),
+ Reviewer("Kenneth Rohde Christiansen", ["kenneth@webkit.org", "kenneth.christiansen@openbossa.org"], "kenne"),
+ Reviewer("Kevin Decker", "kdecker@apple.com", "superkevin"),
+ Reviewer("Kevin McCullough", "kmccullough@apple.com", "maculloch"),
+ Reviewer("Kevin Ollivier", ["kevino@theolliviers.com", "kevino@webkit.org"], "kollivier"),
+ Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org"], "lars"),
+ Reviewer("Laszlo Gombos", "laszlo.1.gombos@nokia.com", "lgombos"),
+ Reviewer("Maciej Stachowiak", "mjs@apple.com", "othermaciej"),
+ Reviewer("Mark Rowe", "mrowe@apple.com", "bdash"),
+ Reviewer("Nate Chapin", "japhet@chromium.org", "japhet"),
+ Reviewer("Nikolas Zimmermann", ["zimmermann@kde.org", "zimmermann@physik.rwth-aachen.de", "zimmermann@webkit.org"], "wildfox"),
+ Reviewer("Oliver Hunt", "oliver@apple.com", "olliej"),
+ Reviewer("Pavel Feldman", "pfeldman@chromium.org", "pfeldman"),
+ Reviewer("Richard Williamson", "rjw@apple.com", "rjw"),
+ Reviewer("Rob Buis", ["rwlbuis@gmail.com", "rwlbuis@webkit.org"], "rwlbuis"),
+ Reviewer("Sam Weinig", ["sam@webkit.org", "weinig@apple.com"], "weinig"),
+ Reviewer("Shinichiro Hamaji", "hamaji@chromium.org", "hamaji"),
+ Reviewer("Simon Fraser", "simon.fraser@apple.com", "smfr"),
+ Reviewer("Simon Hausmann", ["hausmann@webkit.org", "hausmann@kde.org", "simon.hausmann@nokia.com"], "tronical"),
+ Reviewer("Stephanie Lewis", "slewis@apple.com", "sundiamonde"),
+ Reviewer("Steve Falkenburg", "sfalken@apple.com", "sfalken"),
Reviewer("Tim Omernick", "timo@apple.com"),
- Reviewer("Timothy Hatcher", ["timothy@hatcher.name", "timothy@apple.com"]),
- Reviewer(u'Tor Arne Vestb\xf8', "vestbo@webkit.org"),
+ Reviewer("Timothy Hatcher", ["timothy@hatcher.name", "timothy@apple.com"], "xenon"),
+ Reviewer(u'Tor Arne Vestb\xf8', "vestbo@webkit.org", "torarne"),
Reviewer("Vicki Murley", "vicki@apple.com"),
- Reviewer("Xan Lopez", ["xan.lopez@gmail.com", "xan@gnome.org", "xan@webkit.org"]),
- Reviewer("Zack Rusin", "zack@kde.org"),
+ Reviewer("Xan Lopez", ["xan.lopez@gmail.com", "xan@gnome.org", "xan@webkit.org"], "xan"),
+ Reviewer("Yury Semikhatsky", "yurys@chromium.org", "yurys"),
+ Reviewer("Zack Rusin", "zack@kde.org", "zackr"),
]
@@ -260,6 +272,12 @@ class CommitterList:
self._committers_by_email[email] = committer
return self._committers_by_email
+ def committer_by_name(self, name):
+ # This could be made into a hash lookup if callers need it to be fast.
+ for committer in self.committers():
+ if committer.full_name == name:
+ return committer
+
def committer_by_email(self, email):
return self._email_to_committer_map().get(email)
diff --git a/WebKitTools/Scripts/webkitpy/committers_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py
index f5dc539..068c0ee 100644
--- a/WebKitTools/Scripts/webkitpy/committers_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py
@@ -27,12 +27,12 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
-from committers import CommitterList, Committer, Reviewer
+from webkitpy.common.config.committers import CommitterList, Committer, Reviewer
class CommittersTest(unittest.TestCase):
def test_committer_lookup(self):
- committer = Committer('Test One', 'one@test.com')
+ committer = Committer('Test One', 'one@test.com', 'one')
reviewer = Reviewer('Test Two', ['two@test.com', 'two@rad.com', 'so_two@gmail.com'])
committer_list = CommitterList(committers=[committer], reviewers=[reviewer])
@@ -43,6 +43,11 @@ class CommittersTest(unittest.TestCase):
self.assertEqual(committer_list.committer_by_email('two@rad.com'), reviewer)
self.assertEqual(committer_list.reviewer_by_email('so_two@gmail.com'), reviewer)
+ # Test valid committer and reviewer lookup
+ self.assertEqual(committer_list.committer_by_name("Test One"), committer)
+ self.assertEqual(committer_list.committer_by_name("Test Two"), reviewer)
+ self.assertEqual(committer_list.committer_by_name("Test Three"), None)
+
# Test that the first email is assumed to be the Bugzilla email address (for now)
self.assertEqual(committer_list.committer_by_email('two@rad.com').bugzilla_email(), 'two@test.com')
@@ -56,6 +61,8 @@ class CommittersTest(unittest.TestCase):
# Test that emails returns a list.
self.assertEqual(committer.emails, ['one@test.com'])
+ self.assertEqual(committer.irc_nickname, 'one')
+
# Test that committers returns committers and reviewers and reviewers() just reviewers.
self.assertEqual(committer_list.committers(), [committer, reviewer])
self.assertEqual(committer_list.reviewers(), [reviewer])
diff --git a/WebKitTools/Scripts/webkitpy/common/config/irc.py b/WebKitTools/Scripts/webkitpy/common/config/irc.py
new file mode 100644
index 0000000..950c573
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/config/irc.py
@@ -0,0 +1,31 @@
+# Copyright (c) 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.
+
+server="irc.freenode.net"
+port=6667
+channel="#webkit"
diff --git a/WebKitTools/Scripts/webkitpy/webkitport.py b/WebKitTools/Scripts/webkitpy/common/config/ports.py
index cd60a54..a881a67 100644
--- a/WebKitTools/Scripts/webkitpy/webkitport.py
+++ b/WebKitTools/Scripts/webkitpy/common/config/ports.py
@@ -29,9 +29,9 @@
# WebKit's Python module for understanding the various ports
import os
+import platform
-from optparse import make_option
-from webkitpy.executive import Executive
+from webkitpy.common.system.executive import Executive
class WebKitPort(object):
@@ -47,10 +47,22 @@ class WebKitPort(object):
"chromium": ChromiumPort,
"gtk": GtkPort,
"mac": MacPort,
+ "win": WinPort,
"qt": QtPort,
}
- # FIXME: We should default to WinPort on Windows.
- return ports.get(port_name, MacPort)
+ default_port = {
+ "Windows": WinPort,
+ "Darwin": MacPort,
+ }
+ # Do we really need MacPort as the ultimate default?
+ return ports.get(port_name, default_port.get(platform.system(), MacPort))
+
+ @staticmethod
+ def makeArgs():
+ args = '--makeargs="-j%s"' % Executive().cpu_count()
+ if os.environ.has_key('MAKEFLAGS'):
+ args = '--makeargs="%s"' % os.environ['MAKEFLAGS']
+ return args
@classmethod
def name(cls):
@@ -101,6 +113,18 @@ class MacPort(WebKitPort):
return "--port=mac"
+class WinPort(WebKitPort):
+
+ @classmethod
+ def name(cls):
+ return "Win"
+
+ @classmethod
+ def flag(cls):
+ # FIXME: This is lame. We should autogenerate this from a codename or something.
+ return "--port=win"
+
+
class GtkPort(WebKitPort):
@classmethod
@@ -115,7 +139,7 @@ class GtkPort(WebKitPort):
def build_webkit_command(cls, build_style=None):
command = WebKitPort.build_webkit_command(build_style=build_style)
command.append("--gtk")
- command.append('--makeargs="-j%s"' % Executive.cpu_count())
+ command.append(WebKitPort.makeArgs())
return command
@classmethod
@@ -139,7 +163,7 @@ class QtPort(WebKitPort):
def build_webkit_command(cls, build_style=None):
command = WebKitPort.build_webkit_command(build_style=build_style)
command.append("--qt")
- command.append('--makeargs="-j%s"' % Executive.cpu_count())
+ command.append(WebKitPort.makeArgs())
return command
diff --git a/WebKitTools/Scripts/webkitpy/webkitport_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py
index 202234f..c42d2d0 100644
--- a/WebKitTools/Scripts/webkitpy/webkitport_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py
@@ -29,8 +29,7 @@
import unittest
-from webkitpy.executive import Executive
-from webkitpy.webkitport import WebKitPort, MacPort, GtkPort, QtPort, ChromiumPort
+from webkitpy.common.config.ports import WebKitPort, MacPort, GtkPort, QtPort, ChromiumPort
class WebKitPortTest(unittest.TestCase):
@@ -46,15 +45,15 @@ class WebKitPortTest(unittest.TestCase):
self.assertEquals(GtkPort.name(), "Gtk")
self.assertEquals(GtkPort.flag(), "--port=gtk")
self.assertEquals(GtkPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests"), "--gtk"])
- self.assertEquals(GtkPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--gtk", '--makeargs="-j%s"' % Executive.cpu_count()])
- self.assertEquals(GtkPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--gtk", '--makeargs="-j%s"' % Executive.cpu_count()])
+ self.assertEquals(GtkPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--gtk", WebKitPort.makeArgs()])
+ self.assertEquals(GtkPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--gtk", WebKitPort.makeArgs()])
def test_qt_port(self):
self.assertEquals(QtPort.name(), "Qt")
self.assertEquals(QtPort.flag(), "--port=qt")
self.assertEquals(QtPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests")])
- self.assertEquals(QtPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--qt", '--makeargs="-j%s"' % Executive.cpu_count()])
- self.assertEquals(QtPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--qt", '--makeargs="-j%s"' % Executive.cpu_count()])
+ self.assertEquals(QtPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--qt", WebKitPort.makeArgs()])
+ self.assertEquals(QtPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--qt", WebKitPort.makeArgs()])
def test_chromium_port(self):
self.assertEquals(ChromiumPort.name(), "Chromium")
diff --git a/WebKitTools/Scripts/webkitpy/common/net/__init__.py b/WebKitTools/Scripts/webkitpy/common/net/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/bugzilla.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py
index 4506af2..6920d67 100644
--- a/WebKitTools/Scripts/webkitpy/bugzilla.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py
@@ -30,25 +30,24 @@
#
# WebKit's Python module for interacting with Bugzilla
+import os.path
import re
import subprocess
from datetime import datetime # used in timestamp()
-# Import WebKit-specific modules.
-from webkitpy.webkit_logging import error, log
-from webkitpy.committers import CommitterList
-from webkitpy.credentials import Credentials
-from webkitpy.user import User
-
-# WebKit includes a built copy of BeautifulSoup in Scripts/webkitpy
-# so this import should always succeed.
-from .BeautifulSoup import BeautifulSoup, SoupStrainer
-
-from mechanize import Browser
+from webkitpy.common.system.deprecated_logging import error, log
+from webkitpy.common.config import committers
+from webkitpy.common.net.credentials import Credentials
+from webkitpy.common.system.ospath import relpath
+from webkitpy.common.system.user import User
+from webkitpy.thirdparty.autoinstalled.mechanize import Browser
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, SoupStrainer
def parse_bug_id(message):
+ if not message:
+ return None
match = re.search("http\://webkit\.org/b/(?P<bug_id>\d+)", message)
if match:
return int(match.group('bug_id'))
@@ -66,6 +65,8 @@ def timestamp():
class Attachment(object):
+ rollout_preamble = "ROLLOUT of r"
+
def __init__(self, attachment_dictionary, bug):
self._attachment_dictionary = attachment_dictionary
self._bug = bug
@@ -97,6 +98,9 @@ class Attachment(object):
def is_obsolete(self):
return not not self._attachment_dictionary.get("is_obsolete")
+ def is_rollout(self):
+ return self.name().startswith(self.rollout_preamble)
+
def name(self):
return self._attachment_dictionary.get("name")
@@ -150,6 +154,14 @@ class Bug(object):
def assigned_to_email(self):
return self.bug_dictionary["assigned_to_email"]
+ # FIXME: This information should be stored in some sort of webkit_config.py instead of here.
+ unassigned_emails = frozenset([
+ "webkit-unassigned@lists.webkit.org",
+ "webkit-qt-unassigned@trolltech.com",
+ ])
+ def is_unassigned(self):
+ return self.assigned_to_email() in self.unassigned_emails
+
# Rarely do we actually want obsolete attachments
def attachments(self, include_obsolete=False):
attachments = self.bug_dictionary["attachments"]
@@ -218,6 +230,14 @@ class BugzillaQueries(object):
def _fetch_attachment_ids_request_query(self, query):
return self._parse_attachment_ids_request_query(self._load_query(query))
+ def _parse_quips(self, page):
+ soup = BeautifulSoup(page, convertEntities=BeautifulSoup.HTML_ENTITIES)
+ quips = soup.find(text=re.compile(r"Existing quips:")).findNext("ul").findAll("li")
+ return [unicode(quip_entry.string) for quip_entry in quips]
+
+ def fetch_quips(self):
+ return self._parse_quips(self._load_query("/quips.cgi?action=show"))
+
# List of all r+'d bugs.
def fetch_bug_ids_from_pending_commit_list(self):
needs_commit_query_url = "buglist.cgi?query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=flagtypes.name&type0-0-0=equals&value0-0-0=review%2B"
@@ -264,23 +284,37 @@ class CommitterValidator(object):
def _view_source_url(self, local_path):
return "http://trac.webkit.org/browser/trunk/%s" % local_path
+ def _checkout_root(self):
+ # FIXME: This is a hack, we would have this from scm.checkout_root
+ # if we had any way to get to an scm object here.
+ components = __file__.split(os.sep)
+ tools_index = components.index("WebKitTools")
+ return os.sep.join(components[:tools_index])
+
+ def _committers_py_path(self):
+ # extension can sometimes be .pyc, we always want .py
+ (path, extension) = os.path.splitext(committers.__file__)
+ # FIXME: When we're allowed to use python 2.6 we can use the real
+ # os.path.relpath
+ path = relpath(path, self._checkout_root())
+ return ".".join([path, "py"])
+
def _flag_permission_rejection_message(self, setter_email, flag_name):
- # This could be computed from CommitterList.__file__
- committer_list = "WebKitTools/Scripts/webkitpy/committers.py"
# Should come from some webkit_config.py
contribution_guidlines = "http://webkit.org/coding/contributing.html"
# This could be queried from the status_server.
queue_administrator = "eseidel@chromium.org"
# This could be queried from the tool.
queue_name = "commit-queue"
+ committers_list = self._committers_py_path()
message = "%s does not have %s permissions according to %s." % (
setter_email,
flag_name,
- self._view_source_url(committer_list))
+ self._view_source_url(committers_list))
message += "\n\n- If you do not have %s rights please read %s for instructions on how to use bugzilla flags." % (
flag_name, contribution_guidlines)
message += "\n\n- If you have %s rights please correct the error in %s by adding yourself to the file (no review needed). " % (
- flag_name, committer_list)
+ flag_name, committers_list)
message += "Due to bug 30084 the %s will require a restart after your change. " % queue_name
message += "Please contact %s to request a %s restart. " % (
queue_administrator, queue_name)
@@ -333,11 +367,12 @@ class CommitterValidator(object):
class Bugzilla(object):
- def __init__(self, dryrun=False, committers=CommitterList()):
+ def __init__(self, dryrun=False, committers=committers.CommitterList()):
self.dryrun = dryrun
self.authenticated = False
self.queries = BugzillaQueries(self)
self.committers = committers
+ self.cached_quips = []
# FIXME: We should use some sort of Browser mock object when in dryrun
# mode (to prevent any mistakes).
@@ -350,18 +385,30 @@ class Bugzilla(object):
bug_server_host = "bugs.webkit.org"
bug_server_regex = "https?://%s/" % re.sub('\.', '\\.', bug_server_host)
bug_server_url = "https://%s/" % bug_server_host
- unassigned_email = "webkit-unassigned@lists.webkit.org"
+
+ def quips(self):
+ # We only fetch and parse the list of quips once per instantiation
+ # so that we do not burden bugs.webkit.org.
+ if not self.cached_quips and not self.dryrun:
+ self.cached_quips = self.queries.fetch_quips()
+ return self.cached_quips
def bug_url_for_bug_id(self, bug_id, xml=False):
+ if not bug_id:
+ return None
content_type = "&ctype=xml" if xml else ""
return "%sshow_bug.cgi?id=%s%s" % (self.bug_server_url,
bug_id,
content_type)
def short_bug_url_for_bug_id(self, bug_id):
+ if not bug_id:
+ return None
return "http://webkit.org/b/%s" % bug_id
def attachment_url_for_id(self, attachment_id, action="view"):
+ if not attachment_id:
+ return None
action_param = ""
if action and action != "view":
action_param = "&action=%s" % action
@@ -418,7 +465,11 @@ class Bugzilla(object):
return self.browser.open(bug_url)
def fetch_bug_dictionary(self, bug_id):
- return self._parse_bug_page(self._fetch_bug_page(bug_id))
+ try:
+ return self._parse_bug_page(self._fetch_bug_page(bug_id))
+ except:
+ self.authenticate()
+ return self._parse_bug_page(self._fetch_bug_page(bug_id))
# FIXME: A BugzillaCache object should provide all these fetch_ methods.
@@ -554,15 +605,6 @@ class Bugzilla(object):
self.browser['comment'] = comment_text
self.browser.submit()
- def prompt_for_component(self, components):
- log("Please pick a component:")
- i = 0
- for name in components:
- i += 1
- log("%2d. %s" % (i, name))
- result = int(User.prompt("Enter a number: ")) - 1
- return components[result]
-
def _check_create_bug_response(self, response_html):
match = re.search("<title>Bug (?P<bug_id>\d+) Submitted</title>",
response_html)
@@ -589,6 +631,7 @@ class Bugzilla(object):
patch_file_object=None,
patch_description=None,
cc=None,
+ blocked=None,
mark_for_review=False,
mark_for_commit_queue=False):
self.authenticate()
@@ -605,12 +648,14 @@ class Bugzilla(object):
if not component:
component = "New Bugs"
if component not in component_names:
- component = self.prompt_for_component(component_names)
- self.browser['component'] = [component]
+ component = User.prompt_with_list("Please pick a component:", component_names)
+ self.browser["component"] = [component]
if cc:
- self.browser['cc'] = cc
- self.browser['short_desc'] = bug_title
- self.browser['comment'] = bug_description
+ self.browser["cc"] = cc
+ if blocked:
+ self.browser["blocked"] = str(blocked)
+ self.browser["short_desc"] = bug_title
+ self.browser["comment"] = bug_description
if patch_file_object:
self._fill_attachment_form(
diff --git a/WebKitTools/Scripts/webkitpy/bugzilla_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py
index d555f78..4c44cdf 100644
--- a/WebKitTools/Scripts/webkitpy/bugzilla_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py
@@ -28,12 +28,11 @@
import unittest
-from webkitpy.committers import CommitterList, Reviewer, Committer
-from webkitpy.bugzilla import Bugzilla, BugzillaQueries, parse_bug_id, CommitterValidator
-from webkitpy.outputcapture import OutputCapture
-from webkitpy.mock import Mock
-
-from webkitpy.BeautifulSoup import BeautifulSoup
+from webkitpy.common.config.committers import CommitterList, Reviewer, Committer
+from webkitpy.common.net.bugzilla import Bugzilla, BugzillaQueries, parse_bug_id, CommitterValidator, Bug
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
class MockBrowser(object):
@@ -49,14 +48,25 @@ class MockBrowser(object):
def submit(self):
pass
+
+class BugTest(unittest.TestCase):
+ def test_is_unassigned(self):
+ for email in Bug.unassigned_emails:
+ bug = Bug({"assigned_to_email" : email}, bugzilla=None)
+ self.assertTrue(bug.is_unassigned())
+ bug = Bug({"assigned_to_email" : "test@test.com"}, bugzilla=None)
+ self.assertFalse(bug.is_unassigned())
+
+
class CommitterValidatorTest(unittest.TestCase):
def test_flag_permission_rejection_message(self):
validator = CommitterValidator(bugzilla=None)
- expected_messsage="""foo@foo.com does not have review permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/committers.py.
+ self.assertEqual(validator._committers_py_path(), "WebKitTools/Scripts/webkitpy/common/config/committers.py")
+ expected_messsage="""foo@foo.com does not have review permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.
- If you do not have review rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.
-- If you have review rights please correct the error in WebKitTools/Scripts/webkitpy/committers.py by adding yourself to the file (no review needed). Due to bug 30084 the commit-queue will require a restart after your change. Please contact eseidel@chromium.org to request a commit-queue restart. After restart the commit-queue will correctly respect your review rights."""
+- If you have review rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). Due to bug 30084 the commit-queue will require a restart after your change. Please contact eseidel@chromium.org to request a commit-queue restart. After restart the commit-queue will correctly respect your review rights."""
self.assertEqual(validator._flag_permission_rejection_message("foo@foo.com", "review"), expected_messsage)
@@ -101,6 +111,13 @@ class BugzillaTest(unittest.TestCase):
'attacher_email' : 'christian.plesner.hansen@gmail.com',
}
+ def test_url_creation(self):
+ # FIXME: These would be all better as doctests
+ bugs = Bugzilla()
+ self.assertEquals(None, bugs.bug_url_for_bug_id(None))
+ self.assertEquals(None, bugs.short_bug_url_for_bug_id(None))
+ self.assertEquals(None, bugs.attachment_url_for_id(None))
+
def test_parse_bug_id(self):
# FIXME: These would be all better as doctests
bugs = Bugzilla()
@@ -199,6 +216,7 @@ ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg==
}],
}
+ # FIXME: This should move to a central location and be shared by more unit tests.
def _assert_dictionaries_equal(self, actual, expected):
# Make sure we aren't parsing more or less than we expect
self.assertEquals(sorted(actual.keys()), sorted(expected.keys()))
@@ -289,11 +307,37 @@ class BugzillaQueriesTest(unittest.TestCase):
</body>
</html>
"""
+ _sample_quip_page = u"""
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title>Bugzilla Quip System</title>
+ </head>
+ <body>
+ <h2>
+
+ Existing quips:
+ </h2>
+ <ul>
+ <li>Everything should be made as simple as possible, but not simpler. - Albert Einstein</li>
+ <li>Good artists copy. Great artists steal. - Pablo Picasso</li>
+ <li>\u00e7gua mole em pedra dura, tanto bate at\u008e que fura.</li>
+
+ </ul>
+ </body>
+</html>
+"""
def test_request_page_parsing(self):
queries = BugzillaQueries(None)
self.assertEquals([40511, 40722, 40723], queries._parse_attachment_ids_request_query(self._sample_request_page))
+ def test_quip_page_parsing(self):
+ queries = BugzillaQueries(None)
+ expected_quips = ["Everything should be made as simple as possible, but not simpler. - Albert Einstein", "Good artists copy. Great artists steal. - Pablo Picasso", u"\u00e7gua mole em pedra dura, tanto bate at\u008e que fura."]
+ self.assertEquals(expected_quips, queries._parse_quips(self._sample_quip_page))
+
def test_load_query(self):
queries = BugzillaQueries(Mock())
queries._load_query("request.cgi?action=queue&type=review&group=type")
diff --git a/WebKitTools/Scripts/webkitpy/common/net/buildbot.py b/WebKitTools/Scripts/webkitpy/common/net/buildbot.py
new file mode 100644
index 0000000..753e909
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/buildbot.py
@@ -0,0 +1,495 @@
+# Copyright (c) 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.
+#
+# WebKit's Python module for interacting with WebKit's buildbot
+
+import operator
+import re
+import urllib
+import urllib2
+import xmlrpclib
+
+from webkitpy.common.system.logutils import get_logger
+from webkitpy.thirdparty.autoinstalled.mechanize import Browser
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
+
+
+_log = get_logger(__file__)
+
+
+class Builder(object):
+ def __init__(self, name, buildbot):
+ self._name = unicode(name)
+ self._buildbot = buildbot
+ self._builds_cache = {}
+ self._revision_to_build_number = None
+ self._browser = Browser()
+ self._browser.set_handle_robots(False) # The builder pages are excluded by robots.txt
+
+ def name(self):
+ return self._name
+
+ def results_url(self):
+ return "http://%s/results/%s" % (self._buildbot.buildbot_host, self.url_encoded_name())
+
+ def url_encoded_name(self):
+ return urllib.quote(self._name)
+
+ def url(self):
+ return "http://%s/builders/%s" % (self._buildbot.buildbot_host, self.url_encoded_name())
+
+ # This provides a single place to mock
+ def _fetch_build(self, build_number):
+ build_dictionary = self._buildbot._fetch_xmlrpc_build_dictionary(self, build_number)
+ if not build_dictionary:
+ return None
+ return Build(self,
+ build_number=int(build_dictionary['number']),
+ revision=int(build_dictionary['revision']),
+ is_green=(build_dictionary['results'] == 0) # Undocumented, buildbot XMLRPC, 0 seems to mean "pass"
+ )
+
+ def build(self, build_number):
+ if not build_number:
+ return None
+ cached_build = self._builds_cache.get(build_number)
+ if cached_build:
+ return cached_build
+
+ build = self._fetch_build(build_number)
+ self._builds_cache[build_number] = build
+ return build
+
+ def force_build(self, username="webkit-patch", comments=None):
+ def predicate(form):
+ try:
+ return form.find_control("username")
+ except Exception, e:
+ return False
+ self._browser.open(self.url())
+ self._browser.select_form(predicate=predicate)
+ self._browser["username"] = username
+ if comments:
+ self._browser["comments"] = comments
+ return self._browser.submit()
+
+ file_name_regexp = re.compile(r"r(?P<revision>\d+) \((?P<build_number>\d+)\)")
+ def _revision_and_build_for_filename(self, filename):
+ # Example: "r47483 (1)/" or "r47483 (1).zip"
+ match = self.file_name_regexp.match(filename)
+ return (int(match.group("revision")), int(match.group("build_number")))
+
+ def _fetch_revision_to_build_map(self):
+ # All _fetch requests go through _buildbot for easier mocking
+ try:
+ # FIXME: This method is horribly slow due to the huge network load.
+ # FIXME: This is a poor way to do revision -> build mapping.
+ # Better would be to ask buildbot through some sort of API.
+ print "Loading revision/build list from %s." % self.results_url()
+ print "This may take a while..."
+ result_files = self._buildbot._fetch_twisted_directory_listing(self.results_url())
+ except urllib2.HTTPError, error:
+ if error.code != 404:
+ raise
+ result_files = []
+
+ # This assumes there was only one build per revision, which is false but we don't care for now.
+ return dict([self._revision_and_build_for_filename(file_info["filename"]) for file_info in result_files])
+
+ def _revision_to_build_map(self):
+ if not self._revision_to_build_number:
+ self._revision_to_build_number = self._fetch_revision_to_build_map()
+ return self._revision_to_build_number
+
+ def revision_build_pairs_with_results(self):
+ return self._revision_to_build_map().items()
+
+ # This assumes there can be only one build per revision, which is false, but we don't care for now.
+ def build_for_revision(self, revision, allow_failed_lookups=False):
+ # NOTE: This lookup will fail if that exact revision was never built.
+ build_number = self._revision_to_build_map().get(int(revision))
+ if not build_number:
+ return None
+ build = self.build(build_number)
+ if not build and allow_failed_lookups:
+ # Builds for old revisions with fail to lookup via buildbot's xmlrpc api.
+ build = Build(self,
+ build_number=build_number,
+ revision=revision,
+ is_green=False,
+ )
+ return build
+
+ def find_failure_transition(self, red_build, look_back_limit=30):
+ if not red_build or red_build.is_green():
+ return (None, None)
+ common_failures = None
+ current_build = red_build
+ build_after_current_build = None
+ look_back_count = 0
+ while current_build:
+ if current_build.is_green():
+ # current_build can't possibly have any failures in common
+ # with red_build because it's green.
+ break
+ results = current_build.layout_test_results()
+ # We treat a lack of results as if all the test failed.
+ # This occurs, for example, when we can't compile at all.
+ if results:
+ failures = set(results.failing_tests())
+ if common_failures == None:
+ common_failures = failures
+ common_failures = common_failures.intersection(failures)
+ if not common_failures:
+ # current_build doesn't have any failures in common with
+ # the red build we're worried about. We assume that any
+ # failures in current_build were due to flakiness.
+ break
+ look_back_count += 1
+ if look_back_count > look_back_limit:
+ return (None, current_build)
+ build_after_current_build = current_build
+ current_build = current_build.previous_build()
+ # We must iterate at least once because red_build is red.
+ assert(build_after_current_build)
+ # Current build must either be green or have no failures in common
+ # with red build, so we've found our failure transition.
+ return (current_build, build_after_current_build)
+
+ # FIXME: This likely does not belong on Builder
+ def suspect_revisions_for_transition(self, last_good_build, first_bad_build):
+ suspect_revisions = range(first_bad_build.revision(),
+ last_good_build.revision(),
+ -1)
+ suspect_revisions.reverse()
+ return suspect_revisions
+
+ def blameworthy_revisions(self, red_build_number, look_back_limit=30, avoid_flakey_tests=True):
+ red_build = self.build(red_build_number)
+ (last_good_build, first_bad_build) = \
+ self.find_failure_transition(red_build, look_back_limit)
+ if not last_good_build:
+ return [] # We ran off the limit of our search
+ # If avoid_flakey_tests, require at least 2 bad builds before we
+ # suspect a real failure transition.
+ if avoid_flakey_tests and first_bad_build == red_build:
+ return []
+ return self.suspect_revisions_for_transition(last_good_build, first_bad_build)
+
+
+# FIXME: This should be unified with all the layout test results code in the layout_tests package
+class LayoutTestResults(object):
+ stderr_key = u'Tests that had stderr output:'
+ fail_key = u'Tests where results did not match expected results:'
+ timeout_key = u'Tests that timed out:'
+ crash_key = u'Tests that caused the DumpRenderTree tool to crash:'
+ missing_key = u'Tests that had no expected results (probably new):'
+
+ expected_keys = [
+ stderr_key,
+ fail_key,
+ crash_key,
+ timeout_key,
+ missing_key,
+ ]
+
+ @classmethod
+ def _parse_results_html(cls, page):
+ parsed_results = {}
+ tables = BeautifulSoup(page).findAll("table")
+ for table in tables:
+ table_title = table.findPreviousSibling("p").string
+ if table_title not in cls.expected_keys:
+ # This Exception should only ever be hit if run-webkit-tests changes its results.html format.
+ raise Exception("Unhandled title: %s" % str(table_title))
+ # We might want to translate table titles into identifiers before storing.
+ parsed_results[table_title] = [row.find("a").string for row in table.findAll("tr")]
+
+ return parsed_results
+
+ @classmethod
+ def _fetch_results_html(cls, base_url):
+ results_html = "%s/results.html" % base_url
+ # FIXME: We need to move this sort of 404 logic into NetworkTransaction or similar.
+ try:
+ page = urllib2.urlopen(results_html)
+ return cls._parse_results_html(page)
+ except urllib2.HTTPError, error:
+ if error.code != 404:
+ raise
+
+ @classmethod
+ def results_from_url(cls, base_url):
+ parsed_results = cls._fetch_results_html(base_url)
+ if not parsed_results:
+ return None
+ return cls(base_url, parsed_results)
+
+ def __init__(self, base_url, parsed_results):
+ self._base_url = base_url
+ self._parsed_results = parsed_results
+
+ def parsed_results(self):
+ return self._parsed_results
+
+ def failing_tests(self):
+ failing_keys = [self.fail_key, self.crash_key, self.timeout_key]
+ return sorted(sum([tests for key, tests in self._parsed_results.items() if key in failing_keys], []))
+
+
+class Build(object):
+ def __init__(self, builder, build_number, revision, is_green):
+ self._builder = builder
+ self._number = build_number
+ self._revision = revision
+ self._is_green = is_green
+ self._layout_test_results = None
+
+ @staticmethod
+ def build_url(builder, build_number):
+ return "%s/builds/%s" % (builder.url(), build_number)
+
+ def url(self):
+ return self.build_url(self.builder(), self._number)
+
+ def results_url(self):
+ results_directory = "r%s (%s)" % (self.revision(), self._number)
+ return "%s/%s" % (self._builder.results_url(), urllib.quote(results_directory))
+
+ def layout_test_results(self):
+ if not self._layout_test_results:
+ self._layout_test_results = LayoutTestResults.results_from_url(self.results_url())
+ return self._layout_test_results
+
+ def builder(self):
+ return self._builder
+
+ def revision(self):
+ return self._revision
+
+ def is_green(self):
+ return self._is_green
+
+ def previous_build(self):
+ # previous_build() allows callers to avoid assuming build numbers are sequential.
+ # They may not be sequential across all master changes, or when non-trunk builds are made.
+ return self._builder.build(self._number - 1)
+
+
+class BuildBot(object):
+ # FIXME: This should move into some sort of webkit_config.py
+ default_host = "build.webkit.org"
+
+ def __init__(self, host=default_host):
+ self.buildbot_host = host
+ self._builder_by_name = {}
+
+ # If any core builder is red we should not be landing patches. Other
+ # builders should be added to this list once they are known to be
+ # reliable.
+ # See https://bugs.webkit.org/show_bug.cgi?id=33296 and related bugs.
+ self.core_builder_names_regexps = [
+ "SnowLeopard.*Build",
+ "SnowLeopard.*Test",
+ "Leopard",
+ "Tiger",
+ "Windows.*Build",
+ "Windows.*Debug.*Test",
+ "GTK",
+ "Qt",
+ "Chromium",
+ ]
+
+ def _parse_last_build_cell(self, builder, cell):
+ status_link = cell.find('a')
+ if status_link:
+ # Will be either a revision number or a build number
+ revision_string = status_link.string
+ # If revision_string has non-digits assume it's not a revision number.
+ builder['built_revision'] = int(revision_string) \
+ if not re.match('\D', revision_string) \
+ else None
+ builder['is_green'] = not re.search('fail', cell.renderContents())
+
+ status_link_regexp = r"builders/(?P<builder_name>.*)/builds/(?P<build_number>\d+)"
+ link_match = re.match(status_link_regexp, status_link['href'])
+ builder['build_number'] = int(link_match.group("build_number"))
+ else:
+ # We failed to find a link in the first cell, just give up. This
+ # can happen if a builder is just-added, the first cell will just
+ # be "no build"
+ # Other parts of the code depend on is_green being present.
+ builder['is_green'] = False
+ builder['built_revision'] = None
+ builder['build_number'] = None
+
+ def _parse_current_build_cell(self, builder, cell):
+ activity_lines = cell.renderContents().split("<br />")
+ builder["activity"] = activity_lines[0] # normally "building" or "idle"
+ # The middle lines document how long left for any current builds.
+ match = re.match("(?P<pending_builds>\d) pending", activity_lines[-1])
+ builder["pending_builds"] = int(match.group("pending_builds")) if match else 0
+
+ def _parse_builder_status_from_row(self, status_row):
+ status_cells = status_row.findAll('td')
+ builder = {}
+
+ # First cell is the name
+ name_link = status_cells[0].find('a')
+ builder["name"] = name_link.string
+
+ self._parse_last_build_cell(builder, status_cells[1])
+ self._parse_current_build_cell(builder, status_cells[2])
+ return builder
+
+ def _matches_regexps(self, builder_name, name_regexps):
+ for name_regexp in name_regexps:
+ if re.match(name_regexp, builder_name):
+ return True
+ return False
+
+ # FIXME: Should move onto Builder
+ def _is_core_builder(self, builder_name):
+ return self._matches_regexps(builder_name, self.core_builder_names_regexps)
+
+ # FIXME: This method needs to die, but is used by a unit test at the moment.
+ def _builder_statuses_with_names_matching_regexps(self, builder_statuses, name_regexps):
+ return [builder for builder in builder_statuses if self._matches_regexps(builder["name"], name_regexps)]
+
+ def red_core_builders(self):
+ return [builder for builder in self.core_builder_statuses() if not builder["is_green"]]
+
+ def red_core_builders_names(self):
+ return [builder["name"] for builder in self.red_core_builders()]
+
+ def idle_red_core_builders(self):
+ return [builder for builder in self.red_core_builders() if builder["activity"] == "idle"]
+
+ def core_builders_are_green(self):
+ return not self.red_core_builders()
+
+ # FIXME: These _fetch methods should move to a networking class.
+ def _fetch_xmlrpc_build_dictionary(self, builder, build_number):
+ # The buildbot XMLRPC API is super-limited.
+ # For one, you cannot fetch info on builds which are incomplete.
+ proxy = xmlrpclib.ServerProxy("http://%s/xmlrpc" % self.buildbot_host, allow_none=True)
+ try:
+ return proxy.getBuild(builder.name(), int(build_number))
+ except xmlrpclib.Fault, err:
+ build_url = Build.build_url(builder, build_number)
+ _log.error("Error fetching data for %s build %s (%s): %s" % (builder.name(), build_number, build_url, err))
+ return None
+
+ def _fetch_one_box_per_builder(self):
+ build_status_url = "http://%s/one_box_per_builder" % self.buildbot_host
+ return urllib2.urlopen(build_status_url)
+
+ def _parse_twisted_file_row(self, file_row):
+ string_or_empty = lambda string: str(string) if string else ""
+ file_cells = file_row.findAll('td')
+ return {
+ "filename" : string_or_empty(file_cells[0].find("a").string),
+ "size" : string_or_empty(file_cells[1].string),
+ "type" : string_or_empty(file_cells[2].string),
+ "encoding" : string_or_empty(file_cells[3].string),
+ }
+
+ def _parse_twisted_directory_listing(self, page):
+ soup = BeautifulSoup(page)
+ # HACK: Match only table rows with a class to ignore twisted header/footer rows.
+ file_rows = soup.find('table').findAll('tr', { "class" : True })
+ return [self._parse_twisted_file_row(file_row) for file_row in file_rows]
+
+ # FIXME: There should be a better way to get this information directly from twisted.
+ def _fetch_twisted_directory_listing(self, url):
+ return self._parse_twisted_directory_listing(urllib2.urlopen(url))
+
+ def builders(self):
+ return [self.builder_with_name(status["name"]) for status in self.builder_statuses()]
+
+ # This method pulls from /one_box_per_builder as an efficient way to get information about
+ def builder_statuses(self):
+ soup = BeautifulSoup(self._fetch_one_box_per_builder())
+ return [self._parse_builder_status_from_row(status_row) for status_row in soup.find('table').findAll('tr')]
+
+ def core_builder_statuses(self):
+ return [builder for builder in self.builder_statuses() if self._is_core_builder(builder["name"])]
+
+ def builder_with_name(self, name):
+ builder = self._builder_by_name.get(name)
+ if not builder:
+ builder = Builder(name, self)
+ self._builder_by_name[name] = builder
+ return builder
+
+ def revisions_causing_failures(self, only_core_builders=True):
+ builder_statuses = self.core_builder_statuses() if only_core_builders else self.builder_statuses()
+ revision_to_failing_bots = {}
+ for builder_status in builder_statuses:
+ if builder_status["is_green"]:
+ continue
+ builder = self.builder_with_name(builder_status["name"])
+ revisions = builder.blameworthy_revisions(builder_status["build_number"])
+ for revision in revisions:
+ failing_bots = revision_to_failing_bots.get(revision, [])
+ failing_bots.append(builder)
+ revision_to_failing_bots[revision] = failing_bots
+ return revision_to_failing_bots
+
+ # This makes fewer requests than calling Builder.latest_build would. It grabs all builder
+ # statuses in one request using self.builder_statuses (fetching /one_box_per_builder instead of builder pages).
+ def _latest_builds_from_builders(self, only_core_builders=True):
+ builder_statuses = self.core_builder_statuses() if only_core_builders else self.builder_statuses()
+ return [self.builder_with_name(status["name"]).build(status["build_number"]) for status in builder_statuses]
+
+ def _build_at_or_before_revision(self, build, revision):
+ while build:
+ if build.revision() <= revision:
+ return build
+ build = build.previous_build()
+
+ def last_green_revision(self, only_core_builders=True):
+ builds = self._latest_builds_from_builders(only_core_builders)
+ target_revision = builds[0].revision()
+ # An alternate way to do this would be to start at one revision and walk backwards
+ # checking builder.build_for_revision, however build_for_revision is very slow on first load.
+ while True:
+ # Make builds agree on revision
+ builds = [self._build_at_or_before_revision(build, target_revision) for build in builds]
+ if None in builds: # One of the builds failed to load from the server.
+ return None
+ min_revision = min(map(lambda build: build.revision(), builds))
+ if min_revision != target_revision:
+ target_revision = min_revision
+ continue # Builds don't all agree on revision, keep searching
+ # Check to make sure they're all green
+ all_are_green = reduce(operator.and_, map(lambda build: build.is_green(), builds))
+ if not all_are_green:
+ target_revision -= 1
+ continue
+ return min_revision
diff --git a/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py
new file mode 100644
index 0000000..f765f6e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py
@@ -0,0 +1,433 @@
+# Copyright (C) 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.
+
+import unittest
+
+from webkitpy.common.net.buildbot import BuildBot, Builder, Build, LayoutTestResults
+
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
+
+
+class BuilderTest(unittest.TestCase):
+ def _install_fetch_build(self, failure):
+ def _mock_fetch_build(build_number):
+ build = Build(
+ builder=self.builder,
+ build_number=build_number,
+ revision=build_number + 1000,
+ is_green=build_number < 4
+ )
+ build._layout_test_results = LayoutTestResults(
+ "http://buildbot.example.com/foo", {
+ LayoutTestResults.fail_key: failure(build_number),
+ })
+ return build
+ self.builder._fetch_build = _mock_fetch_build
+
+ def setUp(self):
+ self.buildbot = BuildBot()
+ self.builder = Builder("Test Builder", self.buildbot)
+ self._install_fetch_build(lambda build_number: ["test1", "test2"])
+
+ def test_find_failure_transition(self):
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build.revision(), 1003)
+ self.assertEqual(red_build.revision(), 1004)
+
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10), look_back_limit=2)
+ self.assertEqual(green_build, None)
+ self.assertEqual(red_build.revision(), 1008)
+
+ def test_none_build(self):
+ self.builder._fetch_build = lambda build_number: None
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build, None)
+ self.assertEqual(red_build, None)
+
+ def test_flaky_tests(self):
+ self._install_fetch_build(lambda build_number: ["test1"] if build_number % 2 else ["test2"])
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build.revision(), 1009)
+ self.assertEqual(red_build.revision(), 1010)
+
+ def test_failure_and_flaky(self):
+ self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number % 2 else ["test2"])
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build.revision(), 1003)
+ self.assertEqual(red_build.revision(), 1004)
+
+ def test_no_results(self):
+ self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number % 2 else ["test2"])
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build.revision(), 1003)
+ self.assertEqual(red_build.revision(), 1004)
+
+ def test_failure_after_flaky(self):
+ self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number > 6 else ["test3"])
+ (green_build, red_build) = self.builder.find_failure_transition(self.builder.build(10))
+ self.assertEqual(green_build.revision(), 1006)
+ self.assertEqual(red_build.revision(), 1007)
+
+ def test_blameworthy_revisions(self):
+ self.assertEqual(self.builder.blameworthy_revisions(10), [1004])
+ self.assertEqual(self.builder.blameworthy_revisions(10, look_back_limit=2), [])
+ # Flakey test avoidance requires at least 2 red builds:
+ self.assertEqual(self.builder.blameworthy_revisions(4), [])
+ self.assertEqual(self.builder.blameworthy_revisions(4, avoid_flakey_tests=False), [1004])
+ # Green builder:
+ self.assertEqual(self.builder.blameworthy_revisions(3), [])
+
+ def test_build_caching(self):
+ self.assertEqual(self.builder.build(10), self.builder.build(10))
+
+ def test_build_and_revision_for_filename(self):
+ expectations = {
+ "r47483 (1)/" : (47483, 1),
+ "r47483 (1).zip" : (47483, 1),
+ }
+ for filename, revision_and_build in expectations.items():
+ self.assertEqual(self.builder._revision_and_build_for_filename(filename), revision_and_build)
+
+
+class LayoutTestResultsTest(unittest.TestCase):
+ _example_results_html = """
+<html>
+<head>
+<title>Layout Test Results</title>
+</head>
+<body>
+<p>Tests that had stderr output:</p>
+<table>
+<tr>
+<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/accessibility/aria-activedescendant-crash.html">accessibility/aria-activedescendant-crash.html</a></td>
+<td><a href="accessibility/aria-activedescendant-crash-stderr.txt">stderr</a></td>
+</tr>
+<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/http/tests/security/canvas-remote-read-svg-image.html">http/tests/security/canvas-remote-read-svg-image.html</a></td>
+<td><a href="http/tests/security/canvas-remote-read-svg-image-stderr.txt">stderr</a></td>
+</tr>
+</table><p>Tests that had no expected results (probably new):</p>
+<table>
+<tr>
+<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/fast/repaint/no-caret-repaint-in-non-content-editable-element.html">fast/repaint/no-caret-repaint-in-non-content-editable-element.html</a></td>
+<td><a href="fast/repaint/no-caret-repaint-in-non-content-editable-element-actual.txt">result</a></td>
+</tr>
+</table></body>
+</html>
+"""
+
+ _expected_layout_test_results = {
+ 'Tests that had stderr output:' : [
+ 'accessibility/aria-activedescendant-crash.html'
+ ],
+ 'Tests that had no expected results (probably new):' : [
+ 'fast/repaint/no-caret-repaint-in-non-content-editable-element.html'
+ ]
+ }
+ def test_parse_layout_test_results(self):
+ results = LayoutTestResults._parse_results_html(self._example_results_html)
+ self.assertEqual(self._expected_layout_test_results, results)
+
+
+class BuildBotTest(unittest.TestCase):
+
+ _example_one_box_status = '''
+ <table>
+ <tr>
+ <td class="box"><a href="builders/Windows%20Debug%20%28Tests%29">Windows Debug (Tests)</a></td>
+ <td align="center" class="LastBuild box success"><a href="builders/Windows%20Debug%20%28Tests%29/builds/3693">47380</a><br />build<br />successful</td>
+ <td align="center" class="Activity building">building<br />ETA in<br />~ 14 mins<br />at 13:40</td>
+ <tr>
+ <td class="box"><a href="builders/SnowLeopard%20Intel%20Release">SnowLeopard Intel Release</a></td>
+ <td class="LastBuild box" >no build</td>
+ <td align="center" class="Activity building">building<br />< 1 min</td>
+ <tr>
+ <td class="box"><a href="builders/Qt%20Linux%20Release">Qt Linux Release</a></td>
+ <td align="center" class="LastBuild box failure"><a href="builders/Qt%20Linux%20Release/builds/654">47383</a><br />failed<br />compile-webkit</td>
+ <td align="center" class="Activity idle">idle<br />3 pending</td>
+ </table>
+'''
+ _expected_example_one_box_parsings = [
+ {
+ 'is_green': True,
+ 'build_number' : 3693,
+ 'name': u'Windows Debug (Tests)',
+ 'built_revision': 47380,
+ 'activity': 'building',
+ 'pending_builds': 0,
+ },
+ {
+ 'is_green': False,
+ 'build_number' : None,
+ 'name': u'SnowLeopard Intel Release',
+ 'built_revision': None,
+ 'activity': 'building',
+ 'pending_builds': 0,
+ },
+ {
+ 'is_green': False,
+ 'build_number' : 654,
+ 'name': u'Qt Linux Release',
+ 'built_revision': 47383,
+ 'activity': 'idle',
+ 'pending_builds': 3,
+ },
+ ]
+
+ def test_status_parsing(self):
+ buildbot = BuildBot()
+
+ soup = BeautifulSoup(self._example_one_box_status)
+ status_table = soup.find("table")
+ input_rows = status_table.findAll('tr')
+
+ for x in range(len(input_rows)):
+ status_row = input_rows[x]
+ expected_parsing = self._expected_example_one_box_parsings[x]
+
+ builder = buildbot._parse_builder_status_from_row(status_row)
+
+ # Make sure we aren't parsing more or less than we expect
+ self.assertEquals(builder.keys(), expected_parsing.keys())
+
+ 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()
+
+ # For complete testing, this list should match the list of builders at build.webkit.org:
+ example_builders = [
+ {'name': u'Tiger Intel Release', },
+ {'name': u'Leopard Intel Release (Build)', },
+ {'name': u'Leopard Intel Release (Tests)', },
+ {'name': u'Leopard Intel Debug (Build)', },
+ {'name': u'Leopard Intel Debug (Tests)', },
+ {'name': u'SnowLeopard Intel Release (Build)', },
+ {'name': u'SnowLeopard Intel Release (Tests)', },
+ {'name': u'SnowLeopard Intel Leaks', },
+ {'name': u'Windows Release (Build)', },
+ {'name': u'Windows Release (Tests)', },
+ {'name': u'Windows Debug (Build)', },
+ {'name': u'Windows Debug (Tests)', },
+ {'name': u'GTK Linux 32-bit Release', },
+ {'name': u'GTK Linux 32-bit Debug', },
+ {'name': u'GTK Linux 64-bit Debug', },
+ {'name': u'GTK Linux 64-bit Release', },
+ {'name': u'Qt Linux Release', },
+ {'name': u'Qt Linux Release minimal', },
+ {'name': u'Qt Linux ARMv5 Release', },
+ {'name': u'Qt Linux ARMv7 Release', },
+ {'name': u'Qt Windows 32-bit Release', },
+ {'name': u'Qt Windows 32-bit Debug', },
+ {'name': u'Chromium Linux Release', },
+ {'name': u'Chromium Mac Release', },
+ {'name': u'Chromium Win Release', },
+ {'name': u'New run-webkit-tests', },
+ ]
+ name_regexps = [
+ "SnowLeopard.*Build",
+ "SnowLeopard.*Test",
+ "Leopard",
+ "Tiger",
+ "Windows.*Build",
+ "Windows.*Debug.*Test",
+ "GTK",
+ "Qt",
+ "Chromium",
+ ]
+ expected_builders = [
+ {'name': u'Tiger Intel Release', },
+ {'name': u'Leopard Intel Release (Build)', },
+ {'name': u'Leopard Intel Release (Tests)', },
+ {'name': u'Leopard Intel Debug (Build)', },
+ {'name': u'Leopard Intel Debug (Tests)', },
+ {'name': u'SnowLeopard Intel Release (Build)', },
+ {'name': u'SnowLeopard Intel Release (Tests)', },
+ {'name': u'Windows Release (Build)', },
+ {'name': u'Windows Debug (Build)', },
+ {'name': u'Windows Debug (Tests)', },
+ {'name': u'GTK Linux 32-bit Release', },
+ {'name': u'GTK Linux 32-bit Debug', },
+ {'name': u'GTK Linux 64-bit Debug', },
+ {'name': u'GTK Linux 64-bit Release', },
+ {'name': u'Qt Linux Release', },
+ {'name': u'Qt Linux Release minimal', },
+ {'name': u'Qt Linux ARMv5 Release', },
+ {'name': u'Qt Linux ARMv7 Release', },
+ {'name': u'Qt Windows 32-bit Release', },
+ {'name': u'Qt Windows 32-bit Debug', },
+ {'name': u'Chromium Linux Release', },
+ {'name': u'Chromium Mac Release', },
+ {'name': u'Chromium Win Release', },
+ ]
+
+ # This test should probably be updated if the default regexp list changes
+ self.assertEquals(buildbot.core_builder_names_regexps, name_regexps)
+
+ builders = buildbot._builder_statuses_with_names_matching_regexps(example_builders, name_regexps)
+ self.assertEquals(builders, expected_builders)
+
+ def test_builder_with_name(self):
+ buildbot = BuildBot()
+
+ builder = buildbot.builder_with_name("Test Builder")
+ self.assertEqual(builder.name(), "Test Builder")
+ self.assertEqual(builder.url(), "http://build.webkit.org/builders/Test%20Builder")
+ self.assertEqual(builder.url_encoded_name(), "Test%20Builder")
+ self.assertEqual(builder.results_url(), "http://build.webkit.org/results/Test%20Builder")
+
+ # Override _fetch_xmlrpc_build_dictionary function to not touch the network.
+ def mock_fetch_xmlrpc_build_dictionary(self, build_number):
+ build_dictionary = {
+ "revision" : 2 * build_number,
+ "number" : int(build_number),
+ "results" : build_number % 2, # 0 means pass
+ }
+ return build_dictionary
+ buildbot._fetch_xmlrpc_build_dictionary = mock_fetch_xmlrpc_build_dictionary
+
+ build = builder.build(10)
+ self.assertEqual(build.builder(), builder)
+ self.assertEqual(build.url(), "http://build.webkit.org/builders/Test%20Builder/builds/10")
+ self.assertEqual(build.results_url(), "http://build.webkit.org/results/Test%20Builder/r20%20%2810%29")
+ self.assertEqual(build.revision(), 20)
+ self.assertEqual(build.is_green(), True)
+
+ build = build.previous_build()
+ self.assertEqual(build.builder(), builder)
+ self.assertEqual(build.url(), "http://build.webkit.org/builders/Test%20Builder/builds/9")
+ self.assertEqual(build.results_url(), "http://build.webkit.org/results/Test%20Builder/r18%20%289%29")
+ self.assertEqual(build.revision(), 18)
+ self.assertEqual(build.is_green(), False)
+
+ self.assertEqual(builder.build(None), None)
+
+ _example_directory_listing = '''
+<h1>Directory listing for /results/SnowLeopard Intel Leaks/</h1>
+
+<table>
+ <thead>
+ <tr>
+ <th>Filename</th>
+ <th>Size</th>
+ <th>Content type</th>
+ <th>Content encoding</th>
+ </tr>
+ </thead>
+ <tbody>
+<tr class="odd">
+ <td><a href="r47483%20%281%29/">r47483 (1)/</a></td>
+ <td></td>
+ <td>[Directory]</td>
+ <td></td>
+</tr>
+<tr class="odd">
+ <td><a href="r47484%20%282%29.zip">r47484 (2).zip</a></td>
+ <td>89K</td>
+ <td>[application/zip]</td>
+ <td></td>
+</tr>
+'''
+ _expected_files = [
+ {
+ "filename" : "r47483 (1)/",
+ "size" : "",
+ "type" : "[Directory]",
+ "encoding" : "",
+ },
+ {
+ "filename" : "r47484 (2).zip",
+ "size" : "89K",
+ "type" : "[application/zip]",
+ "encoding" : "",
+ },
+ ]
+
+ def test_parse_build_to_revision_map(self):
+ buildbot = BuildBot()
+ files = buildbot._parse_twisted_directory_listing(self._example_directory_listing)
+ self.assertEqual(self._expected_files, files)
+
+ # Revision, is_green
+ # Ordered from newest (highest number) to oldest.
+ fake_builder1 = [
+ [2, False],
+ [1, True],
+ ]
+ fake_builder2 = [
+ [2, False],
+ [1, True],
+ ]
+ fake_builders = [
+ fake_builder1,
+ fake_builder2,
+ ]
+ def _build_from_fake(self, fake_builder, index):
+ if index >= len(fake_builder):
+ return None
+ fake_build = fake_builder[index]
+ build = Build(
+ builder=fake_builder,
+ build_number=index,
+ revision=fake_build[0],
+ is_green=fake_build[1],
+ )
+ def mock_previous_build():
+ return self._build_from_fake(fake_builder, index + 1)
+ build.previous_build = mock_previous_build
+ return build
+
+ def _fake_builds_at_index(self, index):
+ return [self._build_from_fake(builder, index) for builder in self.fake_builders]
+
+ def test_last_green_revision(self):
+ buildbot = BuildBot()
+ def mock_builds_from_builders(only_core_builders):
+ return self._fake_builds_at_index(0)
+ buildbot._latest_builds_from_builders = mock_builds_from_builders
+ self.assertEqual(buildbot.last_green_revision(), 1)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/credentials.py b/WebKitTools/Scripts/webkitpy/common/net/credentials.py
index 295c576..1d5f83d 100644
--- a/WebKitTools/Scripts/webkitpy/credentials.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/credentials.py
@@ -34,30 +34,23 @@ import os
import platform
import re
-from webkitpy.executive import Executive, ScriptError
-from webkitpy.webkit_logging import log
-from webkitpy.scm import Git
-from webkitpy.user import User
+from webkitpy.common.checkout.scm import Git
+from webkitpy.common.system.executive import Executive, ScriptError
+from webkitpy.common.system.user import User
+from webkitpy.common.system.deprecated_logging import log
class Credentials(object):
def __init__(self, host, git_prefix=None, executive=None, cwd=os.getcwd()):
self.host = host
- self.git_prefix = git_prefix
+ self.git_prefix = "%s." % git_prefix if git_prefix else ""
self.executive = executive or Executive()
self.cwd = cwd
def _credentials_from_git(self):
- return [self._read_git_config("username"),
- self._read_git_config("password")]
-
- def _read_git_config(self, key):
- config_key = "%s.%s" % (self.git_prefix, key) if self.git_prefix \
- else key
- return self.executive.run_command(
- ["git", "config", "--get", config_key],
- error_handler=Executive.ignore_error).rstrip('\n')
+ return [Git.read_git_config(self.git_prefix + "username"),
+ Git.read_git_config(self.git_prefix + "password")]
def _keychain_value_with_label(self, label, source_text):
match = re.search("%s\"(?P<value>.+)\"" % label,
diff --git a/WebKitTools/Scripts/webkitpy/credentials_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py
index 0bd5340..9a42bdd 100644
--- a/WebKitTools/Scripts/webkitpy/credentials_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py
@@ -29,10 +29,10 @@
import os
import tempfile
import unittest
-from webkitpy.credentials import Credentials
-from webkitpy.executive import Executive
-from webkitpy.outputcapture import OutputCapture
-from webkitpy.mock import Mock
+from webkitpy.common.net.credentials import Credentials
+from webkitpy.common.system.executive import Executive
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
class CredentialsTest(unittest.TestCase):
example_security_output = """keychain: "/Users/test/Library/Keychains/login.keychain"
@@ -101,16 +101,6 @@ password: "SECRETSAUCE"
self._assert_security_call()
self._assert_security_call(username="foo")
- def test_git_config_calls(self):
- executive_mock = Mock()
- credentials = Credentials("example.com", executive=executive_mock)
- credentials._read_git_config("foo")
- executive_mock.run_command.assert_called_with(["git", "config", "--get", "foo"], error_handler=Executive.ignore_error)
-
- credentials = Credentials("example.com", git_prefix="test_prefix", executive=executive_mock)
- credentials._read_git_config("foo")
- executive_mock.run_command.assert_called_with(["git", "config", "--get", "test_prefix.foo"], error_handler=Executive.ignore_error)
-
def test_read_credentials_without_git_repo(self):
class FakeCredentials(Credentials):
def _is_mac_os_x(self):
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py b/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py
new file mode 100644
index 0000000..f742867
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import webkitpy.common.config.irc as config_irc
+
+from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
+from webkitpy.thirdparty.autoinstalled.irc import ircbot
+from webkitpy.thirdparty.autoinstalled.irc import irclib
+
+
+class IRCBotDelegate(object):
+ def irc_message_received(self, nick, message):
+ raise NotImplementedError, "subclasses must implement"
+
+ def irc_nickname(self):
+ raise NotImplementedError, "subclasses must implement"
+
+ def irc_password(self):
+ raise NotImplementedError, "subclasses must implement"
+
+
+class IRCBot(ircbot.SingleServerIRCBot, MessagePumpDelegate):
+ # FIXME: We should get this information from a config file.
+ def __init__(self,
+ message_queue,
+ delegate):
+ self._message_queue = message_queue
+ self._delegate = delegate
+ ircbot.SingleServerIRCBot.__init__(
+ self,
+ [(
+ config_irc.server,
+ config_irc.port,
+ self._delegate.irc_password()
+ )],
+ self._delegate.irc_nickname(),
+ self._delegate.irc_nickname())
+ self._channel = config_irc.channel
+
+ # ircbot.SingleServerIRCBot methods
+
+ def on_nicknameinuse(self, connection, event):
+ connection.nick(connection.get_nickname() + "_")
+
+ def on_welcome(self, connection, event):
+ connection.join(self._channel)
+ self._message_pump = MessagePump(self, self._message_queue)
+
+ def on_pubmsg(self, connection, event):
+ nick = irclib.nm_to_n(event.source())
+ request = event.arguments()[0].split(":", 1)
+ if len(request) > 1 and irclib.irc_lower(request[0]) == irclib.irc_lower(self.connection.get_nickname()):
+ response = self._delegate.irc_message_received(nick, request[1])
+ if response:
+ connection.privmsg(self._channel, response)
+
+ # MessagePumpDelegate methods
+
+ def schedule(self, interval, callback):
+ self.connection.execute_delayed(interval, callback)
+
+ def message_available(self, message):
+ self.connection.privmsg(self._channel, message)
+
+ def final_message_delivered(self):
+ self.die()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py
new file mode 100644
index 0000000..13348b4
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import threading
+
+from webkitpy.common.net.irc.ircbot import IRCBot
+from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
+from webkitpy.common.system.deprecated_logging import log
+
+
+class _IRCThread(threading.Thread):
+ def __init__(self, message_queue, irc_delegate, irc_bot):
+ threading.Thread.__init__(self)
+ self.setDaemon(True)
+ self._message_queue = message_queue
+ self._irc_delegate = irc_delegate
+ self._irc_bot = irc_bot
+
+ def run(self):
+ bot = self._irc_bot(self._message_queue, self._irc_delegate)
+ bot.start()
+
+
+class IRCProxy(object):
+ def __init__(self, irc_delegate, irc_bot=IRCBot):
+ log("Connecting to IRC")
+ self._message_queue = ThreadedMessageQueue()
+ self._child_thread = _IRCThread(self._message_queue, irc_delegate, irc_bot)
+ self._child_thread.start()
+
+ def post(self, message):
+ self._message_queue.post(message)
+
+ def disconnect(self):
+ log("Disconnecting from IRC...")
+ self._message_queue.stop()
+ self._child_thread.join()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py
new file mode 100644
index 0000000..b44ce40
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.net.irc.ircproxy import IRCProxy
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+
+class IRCProxyTest(unittest.TestCase):
+ def test_trivial(self):
+ def fun():
+ proxy = IRCProxy(Mock(), Mock())
+ proxy.post("hello")
+ proxy.disconnect()
+
+ expected_stderr = "Connecting to IRC\nDisconnecting from IRC...\n"
+ OutputCapture().assert_outputs(self, fun, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/networktransaction.py b/WebKitTools/Scripts/webkitpy/common/net/networktransaction.py
index 65ea27d..c82fc6f 100644
--- a/WebKitTools/Scripts/webkitpy/networktransaction.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/networktransaction.py
@@ -26,10 +26,14 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import logging
import time
-from mechanize import HTTPError
-from webkitpy.webkit_logging import log
+from webkitpy.thirdparty.autoinstalled.mechanize import HTTPError
+from webkitpy.common.system.deprecated_logging import log
+
+
+_log = logging.getLogger(__name__)
class NetworkTimeout(Exception):
@@ -37,7 +41,7 @@ class NetworkTimeout(Exception):
class NetworkTransaction(object):
- def __init__(self, initial_backoff_seconds=10, grown_factor=1.1, timeout_seconds=5*60*60):
+ def __init__(self, initial_backoff_seconds=10, grown_factor=1.5, timeout_seconds=10*60):
self._initial_backoff_seconds = initial_backoff_seconds
self._grown_factor = grown_factor
self._timeout_seconds = timeout_seconds
@@ -50,7 +54,8 @@ class NetworkTransaction(object):
return request()
except HTTPError, e:
self._check_for_timeout()
- log("Received HTTP status %s from server. Retrying in %s seconds..." % (e.code, self._backoff_seconds))
+ _log.warn("Received HTTP status %s from server. Retrying in "
+ "%s seconds..." % (e.code, self._backoff_seconds))
self._sleep()
def _check_for_timeout(self):
diff --git a/WebKitTools/Scripts/webkitpy/networktransaction_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py
index 3cffe02..cd0702b 100644
--- a/WebKitTools/Scripts/webkitpy/networktransaction_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py
@@ -28,10 +28,12 @@
import unittest
-from mechanize import HTTPError
-from webkitpy.networktransaction import NetworkTransaction, NetworkTimeout
+from webkitpy.common.net.networktransaction import NetworkTransaction, NetworkTimeout
+from webkitpy.common.system.logtesting import LoggingTestCase
+from webkitpy.thirdparty.autoinstalled.mechanize import HTTPError
-class NetworkTransactionTest(unittest.TestCase):
+
+class NetworkTransactionTest(LoggingTestCase):
exception = Exception("Test exception")
def test_success(self):
@@ -65,6 +67,10 @@ class NetworkTransactionTest(unittest.TestCase):
transaction = NetworkTransaction(initial_backoff_seconds=0)
self.assertEqual(transaction.run(lambda: self._raise_http_error()), 42)
self.assertEqual(self._run_count, 3)
+ self.assertLog(['WARNING: Received HTTP status 500 from server. '
+ 'Retrying in 0 seconds...\n',
+ 'WARNING: Received HTTP status 500 from server. '
+ 'Retrying in 0.0 seconds...\n'])
def test_timeout(self):
self._run_count = 0
diff --git a/WebKitTools/Scripts/webkitpy/common/net/rietveld.py b/WebKitTools/Scripts/webkitpy/common/net/rietveld.py
new file mode 100644
index 0000000..a9e5b1a
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/rietveld.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import re
+import stat
+
+import webkitpy.common.config as config
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.system.executive import ScriptError
+import webkitpy.thirdparty.autoinstalled.rietveld.upload as upload
+
+
+def parse_codereview_issue(message):
+ if not message:
+ return None
+ match = re.search(config.codereview_server_regex +
+ "(?P<codereview_issue>\d+)",
+ message)
+ if match:
+ return int(match.group('codereview_issue'))
+
+
+class Rietveld(object):
+ def __init__(self, executive, dryrun=False):
+ self.dryrun = dryrun
+ self._executive = executive
+ self._upload_py = upload.__file__
+ # Chop off the last character so we modify permissions on the py file instead of the pyc.
+ if os.path.splitext(self._upload_py)[1] == ".pyc":
+ self._upload_py = self._upload_py[:-1]
+ os.chmod(self._upload_py, os.stat(self._upload_py).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
+ def url_for_issue(self, codereview_issue):
+ if not codereview_issue:
+ return None
+ return "%s%s" % (config.codereview_server_url, codereview_issue)
+
+ def post(self, message=None, codereview_issue=None, cc=None):
+ if not message:
+ raise ScriptError("Rietveld requires a message.")
+
+ args = [
+ self._upload_py,
+ "--assume_yes",
+ "--server=%s" % config.codereview_server_host,
+ "--message=%s" % message,
+ ]
+ if codereview_issue:
+ args.append("--issue=%s" % codereview_issue)
+ if cc:
+ args.append("--cc=%s" % cc)
+
+ if self.dryrun:
+ log("Would have run %s" % args)
+ return
+
+ output = self._executive.run_and_throw_if_fail(args)
+ match = re.search("Issue created\. URL: " +
+ config.codereview_server_regex +
+ "(?P<codereview_issue>\d+)",
+ output)
+ if match:
+ return int(match.group('codereview_issue'))
diff --git a/WebKitTools/Scripts/webkitpy/common/net/rietveld_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/rietveld_unittest.py
new file mode 100644
index 0000000..9c5a29e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/net/rietveld_unittest.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.net.rietveld import Rietveld
+from webkitpy.thirdparty.mock import Mock
+
+
+class RietveldTest(unittest.TestCase):
+ def test_url_for_issue(self):
+ rietveld = Rietveld(Mock())
+ self.assertEqual(rietveld.url_for_issue(34223),
+ "https://wkrietveld.appspot.com/34223")
diff --git a/WebKitTools/Scripts/webkitpy/statusserver.py b/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
index ff0ddfa..e8987a9 100644
--- a/WebKitTools/Scripts/webkitpy/statusserver.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
@@ -26,13 +26,10 @@
# (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 webkitpy.networktransaction import NetworkTransaction
-from webkitpy.webkit_logging import log
-from mechanize import Browser
-
-# WebKit includes a built copy of BeautifulSoup in Scripts/webkitpy
-# so this import should always succeed.
-from .BeautifulSoup import BeautifulSoup
+from webkitpy.common.net.networktransaction import NetworkTransaction
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.thirdparty.autoinstalled.mechanize import Browser
+from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
import urllib2
@@ -64,7 +61,7 @@ class StatusServer:
return
self.browser.add_file(results_file, "text/plain", "results.txt", 'results_file')
- def _post_to_server(self, queue_name, status, patch, results_file):
+ def _post_status_to_server(self, queue_name, status, patch, results_file):
if results_file:
# We might need to re-wind the file if we've already tried to post it.
results_file.seek(0)
@@ -72,25 +69,40 @@ class StatusServer:
update_status_url = "%s/update-status" % self.url
self.browser.open(update_status_url)
self.browser.select_form(name="update_status")
- self.browser['queue_name'] = queue_name
+ self.browser["queue_name"] = queue_name
self._add_patch(patch)
- self.browser['status'] = status
+ self.browser["status"] = status
self._add_results_file(results_file)
return self.browser.submit().read() # This is the id of the newly created status object.
- def update_status(self, queue_name, status, patch=None, results_file=None):
- # During unit testing, host is None
- if not self.host:
- return
+ def _post_svn_revision_to_server(self, svn_revision_number, broken_bot):
+ update_svn_revision_url = "%s/update-svn-revision" % self.url
+ self.browser.open(update_svn_revision_url)
+ self.browser.select_form(name="update_svn_revision")
+ self.browser["number"] = str(svn_revision_number)
+ self.browser["broken_bot"] = broken_bot
+ return self.browser.submit().read()
+ def update_status(self, queue_name, status, patch=None, results_file=None):
log(status)
- return NetworkTransaction().run(lambda: self._post_to_server(queue_name, status, patch, results_file))
+ return NetworkTransaction().run(lambda: self._post_status_to_server(queue_name, status, patch, results_file))
- def patch_status(self, queue_name, patch_id):
- update_status_url = "%s/patch-status/%s/%s" % (self.url, queue_name, patch_id)
+ def update_svn_revision(self, svn_revision_number, broken_bot):
+ log("SVN revision: %s broke %s" % (svn_revision_number, broken_bot))
+ return NetworkTransaction().run(lambda: self._post_svn_revision_to_server(svn_revision_number, broken_bot))
+
+ def _fetch_url(self, url):
try:
- return urllib2.urlopen(update_status_url).read()
+ return urllib2.urlopen(url).read()
except urllib2.HTTPError, e:
if e.code == 404:
return None
raise e
+
+ def patch_status(self, queue_name, patch_id):
+ patch_status_url = "%s/patch-status/%s/%s" % (self.url, queue_name, patch_id)
+ return self._fetch_url(patch_status_url)
+
+ def svn_revision(self, svn_revision_number):
+ svn_revision_url = "%s/svn-revision/%s" % (self.url, svn_revision_number)
+ return self._fetch_url(svn_revision_url)
diff --git a/WebKitTools/Scripts/run-chromium-webkit-tests b/WebKitTools/Scripts/webkitpy/common/prettypatch.py
index 221b5aa..8157f9c 100755..100644
--- a/WebKitTools/Scripts/run-chromium-webkit-tests
+++ b/WebKitTools/Scripts/webkitpy/common/prettypatch.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (c) 2010 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
@@ -27,15 +26,30 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Wrapper around webkitpy/layout_tests/run-chromium-webkit-tests.py"""
import os
-import sys
+import tempfile
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
- "webkitpy", "layout_tests"))
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))))
-import run_chromium_webkit_tests
-if __name__ == '__main__':
- options, args = run_chromium_webkit_tests.parse_args()
- run_chromium_webkit_tests.main(options, args)
+class PrettyPatch(object):
+ def __init__(self, executive, checkout_root):
+ self._executive = executive
+ self._checkout_root = checkout_root
+
+ def pretty_diff_file(self, diff):
+ pretty_diff = self.pretty_diff(diff)
+ diff_file = tempfile.NamedTemporaryFile(suffix=".html")
+ diff_file.write(pretty_diff)
+ diff_file.flush()
+ return diff_file
+
+ def pretty_diff(self, diff):
+ pretty_patch_path = os.path.join(self._checkout_root,
+ "BugsSite", "PrettyPatch")
+ prettify_path = os.path.join(pretty_patch_path, "prettify.rb")
+ args = [
+ "ruby",
+ "-I",
+ pretty_patch_path,
+ prettify_path,
+ ]
+ return self._executive.run_command(args, input=diff)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/__init__.py b/WebKitTools/Scripts/webkitpy/common/system/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py b/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py
new file mode 100755
index 0000000..32fd2cf
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py
@@ -0,0 +1,518 @@
+# Copyright (c) 2009, Daniel Krech All rights reserved.
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 the Daniel Krech 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
+# HOLDER 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.
+
+"""Support for automatically downloading Python packages from an URL."""
+
+import logging
+import new
+import os
+import shutil
+import sys
+import tarfile
+import tempfile
+import urllib
+import urlparse
+import zipfile
+import zipimport
+
+_log = logging.getLogger(__name__)
+
+
+class AutoInstaller(object):
+
+ """Supports automatically installing Python packages from an URL.
+
+ Supports uncompressed files, .tar.gz, and .zip formats.
+
+ Basic usage:
+
+ installer = AutoInstaller()
+
+ installer.install(url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
+ url_subpath="pep8-0.5.0/pep8.py")
+ installer.install(url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
+ url_subpath="mechanize")
+
+ """
+
+ def __init__(self, append_to_search_path=False, make_package=True,
+ target_dir=None, temp_dir=None):
+ """Create an AutoInstaller instance, and set up the target directory.
+
+ Args:
+ append_to_search_path: A boolean value of whether to append the
+ target directory to the sys.path search path.
+ make_package: A boolean value of whether to make the target
+ directory a package. This adds an __init__.py file
+ to the target directory -- allowing packages and
+ modules within the target directory to be imported
+ explicitly using dotted module names.
+ target_dir: The directory path to which packages should be installed.
+ Defaults to a subdirectory of the folder containing
+ this module called "autoinstalled".
+ temp_dir: The directory path to use for any temporary files
+ generated while downloading, unzipping, and extracting
+ packages to install. Defaults to a standard temporary
+ location generated by the tempfile module. This
+ parameter should normally be used only for development
+ testing.
+
+ """
+ if target_dir is None:
+ this_dir = os.path.dirname(__file__)
+ target_dir = os.path.join(this_dir, "autoinstalled")
+
+ # Ensure that the target directory exists.
+ self._set_up_target_dir(target_dir, append_to_search_path, make_package)
+
+ self._target_dir = target_dir
+ self._temp_dir = temp_dir
+
+ def _log_transfer(self, message, source, target, log_method=None):
+ """Log a debug message that involves a source and target."""
+ if log_method is None:
+ log_method = _log.debug
+
+ log_method("%s" % message)
+ log_method(' From: "%s"' % source)
+ log_method(' To: "%s"' % target)
+
+ def _create_directory(self, path, name=None):
+ """Create a directory."""
+ log = _log.debug
+
+ name = name + " " if name is not None else ""
+ log('Creating %sdirectory...' % name)
+ log(' "%s"' % path)
+
+ os.makedirs(path)
+
+ def _write_file(self, path, text):
+ """Create a file at the given path with given text.
+
+ This method overwrites any existing file.
+
+ """
+ _log.debug("Creating file...")
+ _log.debug(' "%s"' % path)
+ file = open(path, "w")
+ try:
+ file.write(text)
+ finally:
+ file.close()
+
+ def _set_up_target_dir(self, target_dir, append_to_search_path,
+ make_package):
+ """Set up a target directory.
+
+ Args:
+ target_dir: The path to the target directory to set up.
+ append_to_search_path: A boolean value of whether to append the
+ target directory to the sys.path search path.
+ make_package: A boolean value of whether to make the target
+ directory a package. This adds an __init__.py file
+ to the target directory -- allowing packages and
+ modules within the target directory to be imported
+ explicitly using dotted module names.
+
+ """
+ if not os.path.exists(target_dir):
+ self._create_directory(target_dir, "autoinstall target")
+
+ if append_to_search_path:
+ sys.path.append(target_dir)
+
+ if make_package:
+ init_path = os.path.join(target_dir, "__init__.py")
+ if not os.path.exists(init_path):
+ text = ("# This file is required for Python to search this "
+ "directory for modules.\n")
+ self._write_file(init_path, text)
+
+ def _create_scratch_directory_inner(self, prefix):
+ """Create a scratch directory without exception handling.
+
+ Creates a scratch directory inside the AutoInstaller temp
+ directory self._temp_dir, or inside a platform-dependent temp
+ directory if self._temp_dir is None. Returns the path to the
+ created scratch directory.
+
+ Raises:
+ OSError: [Errno 2] if the containing temp directory self._temp_dir
+ is not None and does not exist.
+
+ """
+ # The tempfile.mkdtemp() method function requires that the
+ # directory corresponding to the "dir" parameter already exist
+ # if it is not None.
+ scratch_dir = tempfile.mkdtemp(prefix=prefix, dir=self._temp_dir)
+ return scratch_dir
+
+ def _create_scratch_directory(self, target_name):
+ """Create a temporary scratch directory, and return its path.
+
+ The scratch directory is generated inside the temp directory
+ of this AutoInstaller instance. This method also creates the
+ temp directory if it does not already exist.
+
+ """
+ prefix = target_name + "_"
+ try:
+ scratch_dir = self._create_scratch_directory_inner(prefix)
+ except OSError:
+ # Handle case of containing temp directory not existing--
+ # OSError: [Errno 2] No such file or directory:...
+ temp_dir = self._temp_dir
+ if temp_dir is None or os.path.exists(temp_dir):
+ raise
+ # Else try again after creating the temp directory.
+ self._create_directory(temp_dir, "autoinstall temp")
+ scratch_dir = self._create_scratch_directory_inner(prefix)
+
+ return scratch_dir
+
+ def _url_downloaded_path(self, target_name):
+ """Return the path to the file containing the URL downloaded."""
+ filename = ".%s.url" % target_name
+ path = os.path.join(self._target_dir, filename)
+ return path
+
+ def _is_downloaded(self, target_name, url):
+ """Return whether a package version has been downloaded."""
+ version_path = self._url_downloaded_path(target_name)
+
+ _log.debug('Checking %s URL downloaded...' % target_name)
+ _log.debug(' "%s"' % version_path)
+
+ if not os.path.exists(version_path):
+ # Then no package version has been downloaded.
+ _log.debug("No URL file found.")
+ return False
+
+ file = open(version_path, "r")
+ try:
+ version = file.read()
+ finally:
+ file.close()
+
+ return version.strip() == url.strip()
+
+ def _record_url_downloaded(self, target_name, url):
+ """Record the URL downloaded to a file."""
+ version_path = self._url_downloaded_path(target_name)
+ _log.debug("Recording URL downloaded...")
+ _log.debug(' URL: "%s"' % url)
+ _log.debug(' To: "%s"' % version_path)
+
+ self._write_file(version_path, url)
+
+ def _extract_targz(self, path, scratch_dir):
+ # tarfile.extractall() extracts to a path without the
+ # trailing ".tar.gz".
+ target_basename = os.path.basename(path[:-len(".tar.gz")])
+ target_path = os.path.join(scratch_dir, target_basename)
+
+ self._log_transfer("Starting gunzip/extract...", path, target_path)
+
+ try:
+ tar_file = tarfile.open(path)
+ except tarfile.ReadError, err:
+ # Append existing Error message to new Error.
+ message = ("Could not open tar file: %s\n"
+ " The file probably does not have the correct format.\n"
+ " --> Inner message: %s"
+ % (path, err))
+ raise Exception(message)
+
+ try:
+ # This is helpful for debugging purposes.
+ _log.debug("Listing tar file contents...")
+ for name in tar_file.getnames():
+ _log.debug(' * "%s"' % name)
+ _log.debug("Extracting gzipped tar file...")
+ tar_file.extractall(target_path)
+ finally:
+ tar_file.close()
+
+ return target_path
+
+ # This is a replacement for ZipFile.extractall(), which is
+ # available in Python 2.6 but not in earlier versions.
+ def _extract_all(self, zip_file, target_dir):
+ self._log_transfer("Extracting zip file...", zip_file, target_dir)
+
+ # This is helpful for debugging purposes.
+ _log.debug("Listing zip file contents...")
+ for name in zip_file.namelist():
+ _log.debug(' * "%s"' % name)
+
+ for name in zip_file.namelist():
+ path = os.path.join(target_dir, name)
+ self._log_transfer("Extracting...", name, path)
+
+ if not os.path.basename(path):
+ # Then the path ends in a slash, so it is a directory.
+ self._create_directory(path)
+ continue
+ # Otherwise, it is a file.
+
+ try:
+ outfile = open(path, 'wb')
+ except IOError, err:
+ # Not all zip files seem to list the directories explicitly,
+ # so try again after creating the containing directory.
+ _log.debug("Got IOError: retrying after creating directory...")
+ dir = os.path.dirname(path)
+ self._create_directory(dir)
+ outfile = open(path, 'wb')
+
+ try:
+ outfile.write(zip_file.read(name))
+ finally:
+ outfile.close()
+
+ def _unzip(self, path, scratch_dir):
+ # zipfile.extractall() extracts to a path without the
+ # trailing ".zip".
+ target_basename = os.path.basename(path[:-len(".zip")])
+ target_path = os.path.join(scratch_dir, target_basename)
+
+ self._log_transfer("Starting unzip...", path, target_path)
+
+ try:
+ zip_file = zipfile.ZipFile(path, "r")
+ except zipfile.BadZipfile, err:
+ message = ("Could not open zip file: %s\n"
+ " --> Inner message: %s"
+ % (path, err))
+ raise Exception(message)
+
+ try:
+ self._extract_all(zip_file, scratch_dir)
+ finally:
+ zip_file.close()
+
+ return target_path
+
+ def _prepare_package(self, path, scratch_dir):
+ """Prepare a package for use, if necessary, and return the new path.
+
+ For example, this method unzips zipped files and extracts
+ tar files.
+
+ Args:
+ path: The path to the downloaded URL contents.
+ scratch_dir: The scratch directory. Note that the scratch
+ directory contains the file designated by the
+ path parameter.
+
+ """
+ # FIXME: Add other natural extensions.
+ if path.endswith(".zip"):
+ new_path = self._unzip(path, scratch_dir)
+ elif path.endswith(".tar.gz"):
+ new_path = self._extract_targz(path, scratch_dir)
+ else:
+ # No preparation is needed.
+ new_path = path
+
+ return new_path
+
+ def _download_to_stream(self, url, stream):
+ """Download an URL to a stream, and return the number of bytes."""
+ try:
+ netstream = urllib.urlopen(url)
+ except IOError, err:
+ # Append existing Error message to new Error.
+ message = ('Could not download Python modules from URL "%s".\n'
+ " Make sure you are connected to the internet.\n"
+ " You must be connected to the internet when "
+ "downloading needed modules for the first time.\n"
+ " --> Inner message: %s"
+ % (url, err))
+ raise IOError(message)
+ code = 200
+ if hasattr(netstream, "getcode"):
+ code = netstream.getcode()
+ if not 200 <= code < 300:
+ raise ValueError("HTTP Error code %s" % code)
+
+ BUFSIZE = 2**13 # 8KB
+ bytes = 0
+ while True:
+ data = netstream.read(BUFSIZE)
+ if not data:
+ break
+ stream.write(data)
+ bytes += len(data)
+ netstream.close()
+ return bytes
+
+ def _download(self, url, scratch_dir):
+ """Download URL contents, and return the download path."""
+ url_path = urlparse.urlsplit(url)[2]
+ url_path = os.path.normpath(url_path) # Removes trailing slash.
+ target_filename = os.path.basename(url_path)
+ target_path = os.path.join(scratch_dir, target_filename)
+
+ self._log_transfer("Starting download...", url, target_path)
+
+ stream = file(target_path, "wb")
+ bytes = self._download_to_stream(url, stream)
+ stream.close()
+
+ _log.debug("Downloaded %s bytes." % bytes)
+
+ return target_path
+
+ def _install(self, scratch_dir, package_name, target_path, url,
+ url_subpath):
+ """Install a python package from an URL.
+
+ This internal method overwrites the target path if the target
+ path already exists.
+
+ """
+ path = self._download(url=url, scratch_dir=scratch_dir)
+ path = self._prepare_package(path, scratch_dir)
+
+ if url_subpath is None:
+ source_path = path
+ else:
+ source_path = os.path.join(path, url_subpath)
+
+ if os.path.exists(target_path):
+ _log.debug('Refreshing install: deleting "%s".' % target_path)
+ if os.path.isdir(target_path):
+ shutil.rmtree(target_path)
+ else:
+ os.remove(target_path)
+
+ self._log_transfer("Moving files into place...", source_path, target_path)
+
+ # The shutil.move() command creates intermediate directories if they
+ # do not exist, but we do not rely on this behavior since we
+ # need to create the __init__.py file anyway.
+ shutil.move(source_path, target_path)
+
+ self._record_url_downloaded(package_name, url)
+
+ def install(self, url, should_refresh=False, target_name=None,
+ url_subpath=None):
+ """Install a python package from an URL.
+
+ Args:
+ url: The URL from which to download the package.
+
+ Optional Args:
+ should_refresh: A boolean value of whether the package should be
+ downloaded again if the package is already present.
+ target_name: The name of the folder or file in the autoinstaller
+ target directory at which the package should be
+ installed. Defaults to the base name of the
+ URL sub-path. This parameter must be provided if
+ the URL sub-path is not specified.
+ url_subpath: The relative path of the URL directory that should
+ be installed. Defaults to the full directory, or
+ the entire URL contents.
+
+ """
+ if target_name is None:
+ if not url_subpath:
+ raise ValueError('The "target_name" parameter must be '
+ 'provided if the "url_subpath" parameter '
+ "is not provided.")
+ # Remove any trailing slashes.
+ url_subpath = os.path.normpath(url_subpath)
+ target_name = os.path.basename(url_subpath)
+
+ target_path = os.path.join(self._target_dir, target_name)
+ if not should_refresh and self._is_downloaded(target_name, url):
+ _log.debug('URL for %s already downloaded. Skipping...'
+ % target_name)
+ _log.debug(' "%s"' % url)
+ return
+
+ self._log_transfer("Auto-installing package: %s" % target_name,
+ url, target_path, log_method=_log.info)
+
+ # The scratch directory is where we will download and prepare
+ # files specific to this install until they are ready to move
+ # into place.
+ scratch_dir = self._create_scratch_directory(target_name)
+
+ try:
+ self._install(package_name=target_name,
+ target_path=target_path,
+ scratch_dir=scratch_dir,
+ url=url,
+ url_subpath=url_subpath)
+ except Exception, err:
+ # Append existing Error message to new Error.
+ message = ("Error auto-installing the %s package to:\n"
+ ' "%s"\n'
+ " --> Inner message: %s"
+ % (target_name, target_path, err))
+ raise Exception(message)
+ finally:
+ _log.debug('Cleaning up: deleting "%s".' % scratch_dir)
+ shutil.rmtree(scratch_dir)
+ _log.debug('Auto-installed %s to:' % target_name)
+ _log.debug(' "%s"' % target_path)
+
+
+if __name__=="__main__":
+
+ # Configure the autoinstall logger to log DEBUG messages for
+ # development testing purposes.
+ console = logging.StreamHandler()
+
+ formatter = logging.Formatter('%(name)s: %(levelname)-8s %(message)s')
+ console.setFormatter(formatter)
+ _log.addHandler(console)
+ _log.setLevel(logging.DEBUG)
+
+ # Use a more visible temp directory for debug purposes.
+ this_dir = os.path.dirname(__file__)
+ target_dir = os.path.join(this_dir, "autoinstalled")
+ temp_dir = os.path.join(target_dir, "Temp")
+
+ installer = AutoInstaller(target_dir=target_dir,
+ temp_dir=temp_dir)
+
+ installer.install(should_refresh=False,
+ target_name="pep8.py",
+ url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
+ url_subpath="pep8-0.5.0/pep8.py")
+ installer.install(should_refresh=False,
+ target_name="mechanize",
+ url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
+ url_subpath="mechanize")
+
diff --git a/WebKitTools/Scripts/webkitpy/webkit_logging.py b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py
index ba1c5eb..ba1c5eb 100644
--- a/WebKitTools/Scripts/webkitpy/webkit_logging.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py
diff --git a/WebKitTools/Scripts/webkitpy/webkit_logging_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py
index b940a4d..2b71803 100644
--- a/WebKitTools/Scripts/webkitpy/webkit_logging_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py
@@ -32,8 +32,8 @@ import StringIO
import tempfile
import unittest
-from webkitpy.executive import ScriptError
-from webkitpy.webkit_logging import *
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.common.system.deprecated_logging import *
class LoggingTest(unittest.TestCase):
@@ -46,7 +46,7 @@ class LoggingTest(unittest.TestCase):
log(log_input)
actual_output = test_stderr.getvalue()
finally:
- original_stderr = original_stderr
+ sys.stderr = original_stderr
self.assertEquals(actual_output, expected_output, "log(\"%s\") expected: %s actual: %s" % (log_input, expected_output, actual_output))
diff --git a/WebKitTools/Scripts/webkitpy/executive.py b/WebKitTools/Scripts/webkitpy/common/system/executive.py
index 50b119b..b6126e4 100644
--- a/WebKitTools/Scripts/webkitpy/executive.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/executive.py
@@ -27,12 +27,20 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+try:
+ # This API exists only in Python 2.6 and higher. :(
+ import multiprocessing
+except ImportError:
+ multiprocessing = None
+
import os
+import platform
import StringIO
+import signal
import subprocess
import sys
-from webkitpy.webkit_logging import tee
+from webkitpy.common.system.deprecated_logging import tee
class ScriptError(Exception):
@@ -96,9 +104,11 @@ class Executive(object):
def run_and_throw_if_fail(self, args, quiet=False):
# Cache the child's output locally so it can be used for error reports.
child_out_file = StringIO.StringIO()
+ tee_stdout = sys.stdout
if quiet:
dev_null = open(os.devnull, "w")
- child_stdout = tee(child_out_file, dev_null if quiet else sys.stdout)
+ tee_stdout = dev_null
+ child_stdout = tee(child_out_file, tee_stdout)
exit_code = self._run_command_with_teed_output(args, child_stdout)
if quiet:
dev_null.close()
@@ -110,17 +120,37 @@ class Executive(object):
raise ScriptError(script_args=args,
exit_code=exit_code,
output=child_output)
+ return child_output
- @staticmethod
- def cpu_count():
- # This API exists only in Python 2.6 and higher. :(
- try:
- import multiprocessing
+ def cpu_count(self):
+ if multiprocessing:
return multiprocessing.cpu_count()
- except (ImportError, NotImplementedError):
- # This quantity is a lie but probably a reasonable guess for modern
- # machines.
- return 2
+ # Darn. We don't have the multiprocessing package.
+ system_name = platform.system()
+ if system_name == "Darwin":
+ return int(self.run_command(["sysctl", "-n", "hw.ncpu"]))
+ elif system_name == "Windows":
+ return int(os.environ.get('NUMBER_OF_PROCESSORS', 1))
+ elif system_name == "Linux":
+ num_cores = os.sysconf("SC_NPROCESSORS_ONLN")
+ if isinstance(num_cores, int) and num_cores > 0:
+ return num_cores
+ # This quantity is a lie but probably a reasonable guess for modern
+ # machines.
+ return 2
+
+ def kill_process(self, pid):
+ if platform.system() == "Windows":
+ # According to http://docs.python.org/library/os.html
+ # os.kill isn't available on Windows. However, when I tried it
+ # using Cygwin, it worked fine. We should investigate whether
+ # we need this platform specific code here.
+ subprocess.call(('taskkill.exe', '/f', '/pid', str(pid)),
+ stdin=open(os.devnull, 'r'),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ return
+ os.kill(pid, signal.SIGKILL)
# Error handlers do not need to be static methods once all callers are
# updated to use an Executive object.
@@ -146,8 +176,14 @@ class Executive(object):
stdin = input
string_to_communicate = None
else:
- stdin = subprocess.PIPE if input else None
- string_to_communicate = input
+ stdin = None
+ if input:
+ stdin = subprocess.PIPE
+ # string_to_communicate seems to need to be a str for proper
+ # communication with shell commands.
+ # See https://bugs.webkit.org/show_bug.cgi?id=37528
+ # For an example of a regresion caused by passing a unicode string through.
+ string_to_communicate = str(input)
if return_stderr:
stderr = subprocess.STDOUT
else:
@@ -160,12 +196,14 @@ class Executive(object):
cwd=cwd)
output = process.communicate(string_to_communicate)[0]
exit_code = process.wait()
+
+ if return_exit_code:
+ return exit_code
+
if exit_code:
script_error = ScriptError(script_args=args,
exit_code=exit_code,
output=output,
cwd=cwd)
(error_handler or self.default_error_handler)(script_error)
- if return_exit_code:
- return exit_code
return output
diff --git a/WebKitTools/Scripts/webkitpy/executive_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py
index f78e301..ac380f8 100644
--- a/WebKitTools/Scripts/webkitpy/executive_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py
@@ -28,7 +28,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
-from webkitpy.executive import Executive, run_command
+
+from webkitpy.common.system.executive import Executive, run_command
class ExecutiveTest(unittest.TestCase):
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logtesting.py b/WebKitTools/Scripts/webkitpy/common/system/logtesting.py
new file mode 100644
index 0000000..e361cb5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/logtesting.py
@@ -0,0 +1,258 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Supports the unit-testing of logging code.
+
+Provides support for unit-testing messages logged using the built-in
+logging module.
+
+Inherit from the LoggingTestCase class for basic testing needs. For
+more advanced needs (e.g. unit-testing methods that configure logging),
+see the TestLogStream class, and perhaps also the LogTesting class.
+
+"""
+
+import logging
+import unittest
+
+
+class TestLogStream(object):
+
+ """Represents a file-like object for unit-testing logging.
+
+ This is meant for passing to the logging.StreamHandler constructor.
+ Log messages captured by instances of this object can be tested
+ using self.assertMessages() below.
+
+ """
+
+ def __init__(self, test_case):
+ """Create an instance.
+
+ Args:
+ test_case: A unittest.TestCase instance.
+
+ """
+ self._test_case = test_case
+ self.messages = []
+ """A list of log messages written to the stream."""
+
+ # Python documentation says that any object passed to the StreamHandler
+ # constructor should support write() and flush():
+ #
+ # http://docs.python.org/library/logging.html#module-logging.handlers
+ def write(self, message):
+ self.messages.append(message)
+
+ def flush(self):
+ pass
+
+ def assertMessages(self, messages):
+ """Assert that the given messages match the logged messages.
+
+ messages: A list of log message strings.
+
+ """
+ self._test_case.assertEquals(messages, self.messages)
+
+
+class LogTesting(object):
+
+ """Supports end-to-end unit-testing of log messages.
+
+ Sample usage:
+
+ class SampleTest(unittest.TestCase):
+
+ def setUp(self):
+ self._log = LogTesting.setUp(self) # Turn logging on.
+
+ def tearDown(self):
+ self._log.tearDown() # Turn off and reset logging.
+
+ def test_logging_in_some_method(self):
+ call_some_method() # Contains calls to _log.info(), etc.
+
+ # Check the resulting log messages.
+ self._log.assertMessages(["INFO: expected message #1",
+ "WARNING: expected message #2"])
+
+ """
+
+ def __init__(self, test_stream, handler):
+ """Create an instance.
+
+ This method should never be called directly. Instances should
+ instead be created using the static setUp() method.
+
+ Args:
+ test_stream: A TestLogStream instance.
+ handler: The handler added to the logger.
+
+ """
+ self._test_stream = test_stream
+ self._handler = handler
+
+ @staticmethod
+ def _getLogger():
+ """Return the logger being tested."""
+ # It is possible we might want to return something other than
+ # the root logger in some special situation. For now, the
+ # root logger seems to suffice.
+ return logging.getLogger()
+
+ @staticmethod
+ def setUp(test_case, logging_level=logging.INFO):
+ """Configure logging for unit testing.
+
+ Configures the root logger to log to a testing log stream.
+ Only messages logged at or above the given level are logged
+ to the stream. Messages logged to the stream are formatted
+ in the following way, for example--
+
+ "INFO: This is a test log message."
+
+ This method should normally be called in the setUp() method
+ of a unittest.TestCase. See the docstring of this class
+ for more details.
+
+ Returns:
+ A LogTesting instance.
+
+ Args:
+ test_case: A unittest.TestCase instance.
+ logging_level: An integer logging level that is the minimum level
+ of log messages you would like to test.
+
+ """
+ stream = TestLogStream(test_case)
+ handler = logging.StreamHandler(stream)
+ handler.setLevel(logging_level)
+ formatter = logging.Formatter("%(levelname)s: %(message)s")
+ handler.setFormatter(formatter)
+
+ # Notice that we only change the root logger by adding a handler
+ # to it. In particular, we do not reset its level using
+ # logger.setLevel(). This ensures that we have not interfered
+ # with how the code being tested may have configured the root
+ # logger.
+ logger = LogTesting._getLogger()
+ logger.addHandler(handler)
+
+ return LogTesting(stream, handler)
+
+ def tearDown(self):
+ """Assert there are no remaining log messages, and reset logging.
+
+ This method asserts that there are no more messages in the array of
+ log messages, and then restores logging to its original state.
+ This method should normally be called in the tearDown() method of a
+ unittest.TestCase. See the docstring of this class for more details.
+
+ """
+ self.assertMessages([])
+ logger = LogTesting._getLogger()
+ logger.removeHandler(self._handler)
+
+ def messages(self):
+ """Return the current list of log messages."""
+ return self._test_stream.messages
+
+ # FIXME: Add a clearMessages() method for cases where the caller
+ # deliberately doesn't want to assert every message.
+
+ # We clear the log messages after asserting since they are no longer
+ # needed after asserting. This serves two purposes: (1) it simplifies
+ # the calling code when we want to check multiple logging calls in a
+ # single test method, and (2) it lets us check in the tearDown() method
+ # that there are no remaining log messages to be asserted.
+ #
+ # The latter ensures that no extra log messages are getting logged that
+ # the caller might not be aware of or may have forgotten to check for.
+ # This gets us a bit more mileage out of our tests without writing any
+ # additional code.
+ def assertMessages(self, messages):
+ """Assert the current array of log messages, and clear its contents.
+
+ Args:
+ messages: A list of log message strings.
+
+ """
+ try:
+ self._test_stream.assertMessages(messages)
+ finally:
+ # We want to clear the array of messages even in the case of
+ # an Exception (e.g. an AssertionError). Otherwise, another
+ # AssertionError can occur in the tearDown() because the
+ # array might not have gotten emptied.
+ self._test_stream.messages = []
+
+
+# This class needs to inherit from unittest.TestCase. Otherwise, the
+# setUp() and tearDown() methods will not get fired for test case classes
+# that inherit from this class -- even if the class inherits from *both*
+# unittest.TestCase and LoggingTestCase.
+#
+# FIXME: Rename this class to LoggingTestCaseBase to be sure that
+# the unittest module does not interpret this class as a unittest
+# test case itself.
+class LoggingTestCase(unittest.TestCase):
+
+ """Supports end-to-end unit-testing of log messages.
+
+ Sample usage:
+
+ class SampleTest(LoggingTestCase):
+
+ def test_logging_in_some_method(self):
+ call_some_method() # Contains calls to _log.info(), etc.
+
+ # Check the resulting log messages.
+ self.assertLog(["INFO: expected message #1",
+ "WARNING: expected message #2"])
+
+ """
+
+ def setUp(self):
+ self._log = LogTesting.setUp(self)
+
+ def tearDown(self):
+ self._log.tearDown()
+
+ def logMessages(self):
+ """Return the current list of log messages."""
+ return self._log.messages()
+
+ # FIXME: Add a clearMessages() method for cases where the caller
+ # deliberately doesn't want to assert every message.
+
+ # See the code comments preceding LogTesting.assertMessages() for
+ # an explanation of why we clear the array of messages after
+ # asserting its contents.
+ def assertLog(self, messages):
+ """Assert the current array of log messages, and clear its contents.
+
+ Args:
+ messages: A list of log message strings.
+
+ """
+ self._log.assertMessages(messages)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logutils.py b/WebKitTools/Scripts/webkitpy/common/system/logutils.py
new file mode 100644
index 0000000..cd4e60f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/logutils.py
@@ -0,0 +1,207 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Supports webkitpy logging."""
+
+# FIXME: Move this file to webkitpy/python24 since logging needs to
+# be configured prior to running version-checking code.
+
+import logging
+import os
+import sys
+
+import webkitpy
+
+
+_log = logging.getLogger(__name__)
+
+# We set these directory paths lazily in get_logger() below.
+_scripts_dir = ""
+"""The normalized, absolute path to the ...Scripts directory."""
+
+_webkitpy_dir = ""
+"""The normalized, absolute path to the ...Scripts/webkitpy directory."""
+
+
+def _normalize_path(path):
+ """Return the given path normalized.
+
+ Converts a path to an absolute path, removes any trailing slashes,
+ removes any extension, and lower-cases it.
+
+ """
+ path = os.path.abspath(path)
+ path = os.path.normpath(path)
+ path = os.path.splitext(path)[0] # Remove the extension, if any.
+ path = path.lower()
+
+ return path
+
+
+# Observe that the implementation of this function does not require
+# the use of any hard-coded strings like "webkitpy", etc.
+#
+# The main benefit this function has over using--
+#
+# _log = logging.getLogger(__name__)
+#
+# is that get_logger() returns the same value even if __name__ is
+# "__main__" -- i.e. even if the module is the script being executed
+# from the command-line.
+def get_logger(path):
+ """Return a logging.logger for the given path.
+
+ Returns:
+ A logger whose name is the name of the module corresponding to
+ the given path. If the module is in webkitpy, the name is
+ the fully-qualified dotted module name beginning with webkitpy....
+ Otherwise, the name is the base name of the module (i.e. without
+ any dotted module name prefix).
+
+ Args:
+ path: The path of the module. Normally, this parameter should be
+ the __file__ variable of the module.
+
+ Sample usage:
+
+ import webkitpy.common.system.logutils as logutils
+
+ _log = logutils.get_logger(__file__)
+
+ """
+ # Since we assign to _scripts_dir and _webkitpy_dir in this function,
+ # we need to declare them global.
+ global _scripts_dir
+ global _webkitpy_dir
+
+ path = _normalize_path(path)
+
+ # Lazily evaluate _webkitpy_dir and _scripts_dir.
+ if not _scripts_dir:
+ # The normalized, absolute path to ...Scripts/webkitpy/__init__.
+ webkitpy_path = _normalize_path(webkitpy.__file__)
+
+ _webkitpy_dir = os.path.split(webkitpy_path)[0]
+ _scripts_dir = os.path.split(_webkitpy_dir)[0]
+
+ if path.startswith(_webkitpy_dir):
+ # Remove the initial Scripts directory portion, so the path
+ # starts with /webkitpy, for example "/webkitpy/init/logutils".
+ path = path[len(_scripts_dir):]
+
+ parts = []
+ while True:
+ (path, tail) = os.path.split(path)
+ if not tail:
+ break
+ parts.insert(0, tail)
+
+ logger_name = ".".join(parts) # For example, webkitpy.common.system.logutils.
+ else:
+ # The path is outside of webkitpy. Default to the basename
+ # without the extension.
+ basename = os.path.basename(path)
+ logger_name = os.path.splitext(basename)[0]
+
+ return logging.getLogger(logger_name)
+
+
+def _default_handlers(stream):
+ """Return a list of the default logging handlers to use.
+
+ Args:
+ stream: See the configure_logging() docstring.
+
+ """
+ # Create the filter.
+ def should_log(record):
+ """Return whether a logging.LogRecord should be logged."""
+ # FIXME: Enable the logging of autoinstall messages once
+ # autoinstall is adjusted. Currently, autoinstall logs
+ # INFO messages when importing already-downloaded packages,
+ # which is too verbose.
+ if record.name.startswith("webkitpy.thirdparty.autoinstall"):
+ return False
+ return True
+
+ logging_filter = logging.Filter()
+ logging_filter.filter = should_log
+
+ # Create the handler.
+ handler = logging.StreamHandler(stream)
+ formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
+ handler.setFormatter(formatter)
+ handler.addFilter(logging_filter)
+
+ return [handler]
+
+
+def configure_logging(logging_level=None, logger=None, stream=None,
+ handlers=None):
+ """Configure logging for standard purposes.
+
+ Returns:
+ A list of references to the logging handlers added to the root
+ logger. This allows the caller to later remove the handlers
+ using logger.removeHandler. This is useful primarily during unit
+ testing where the caller may want to configure logging temporarily
+ and then undo the configuring.
+
+ Args:
+ logging_level: The minimum logging level to log. Defaults to
+ logging.INFO.
+ logger: A logging.logger instance to configure. This parameter
+ should be used only in unit tests. Defaults to the
+ root logger.
+ stream: A file-like object to which to log used in creating the default
+ handlers. The stream must define an "encoding" data attribute,
+ or else logging raises an error. Defaults to sys.stderr.
+ handlers: A list of logging.Handler instances to add to the logger
+ being configured. If this parameter is provided, then the
+ stream parameter is not used.
+
+ """
+ # If the stream does not define an "encoding" data attribute, the
+ # logging module can throw an error like the following:
+ #
+ # Traceback (most recent call last):
+ # File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
+ # lib/python2.6/logging/__init__.py", line 761, in emit
+ # self.stream.write(fs % msg.encode(self.stream.encoding))
+ # LookupError: unknown encoding: unknown
+ if logging_level is None:
+ logging_level = logging.INFO
+ if logger is None:
+ logger = logging.getLogger()
+ if stream is None:
+ stream = sys.stderr
+ if handlers is None:
+ handlers = _default_handlers(stream)
+
+ logger.setLevel(logging_level)
+
+ for handler in handlers:
+ logger.addHandler(handler)
+
+ _log.debug("Debug logging enabled.")
+
+ return handlers
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py
new file mode 100644
index 0000000..a4a6496
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py
@@ -0,0 +1,142 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Unit tests for logutils.py."""
+
+import logging
+import os
+import unittest
+
+from webkitpy.common.system.logtesting import LogTesting
+from webkitpy.common.system.logtesting import TestLogStream
+import webkitpy.common.system.logutils as logutils
+
+
+class GetLoggerTest(unittest.TestCase):
+
+ """Tests get_logger()."""
+
+ def test_get_logger_in_webkitpy(self):
+ logger = logutils.get_logger(__file__)
+ self.assertEquals(logger.name, "webkitpy.common.system.logutils_unittest")
+
+ def test_get_logger_not_in_webkitpy(self):
+ # Temporarily change the working directory so that we
+ # can test get_logger() for a path outside of webkitpy.
+ working_directory = os.getcwd()
+ root_dir = "/"
+ os.chdir(root_dir)
+
+ logger = logutils.get_logger("/WebKitTools/Scripts/test-webkitpy")
+ self.assertEquals(logger.name, "test-webkitpy")
+
+ logger = logutils.get_logger("/WebKitTools/Scripts/test-webkitpy.py")
+ self.assertEquals(logger.name, "test-webkitpy")
+
+ os.chdir(working_directory)
+
+
+class ConfigureLoggingTestBase(unittest.TestCase):
+
+ """Base class for configure_logging() unit tests."""
+
+ def _logging_level(self):
+ raise Exception("Not implemented.")
+
+ def setUp(self):
+ log_stream = TestLogStream(self)
+
+ # Use a logger other than the root logger or one prefixed with
+ # "webkitpy." so as not to conflict with test-webkitpy logging.
+ logger = logging.getLogger("unittest")
+
+ # Configure the test logger not to pass messages along to the
+ # root logger. This prevents test messages from being
+ # propagated to loggers used by test-webkitpy logging (e.g.
+ # the root logger).
+ logger.propagate = False
+
+ logging_level = self._logging_level()
+ self._handlers = logutils.configure_logging(logging_level=logging_level,
+ logger=logger,
+ stream=log_stream)
+ self._log = logger
+ self._log_stream = log_stream
+
+ def tearDown(self):
+ """Reset logging to its original state.
+
+ This method ensures that the logging configuration set up
+ for a unit test does not affect logging in other unit tests.
+
+ """
+ logger = self._log
+ for handler in self._handlers:
+ logger.removeHandler(handler)
+
+ def _assert_log_messages(self, messages):
+ """Assert that the logged messages equal the given messages."""
+ self._log_stream.assertMessages(messages)
+
+
+class ConfigureLoggingTest(ConfigureLoggingTestBase):
+
+ """Tests configure_logging() with the default logging level."""
+
+ def _logging_level(self):
+ return None
+
+ def test_info_message(self):
+ self._log.info("test message")
+ self._assert_log_messages(["unittest: [INFO] test message\n"])
+
+ def test_below_threshold_message(self):
+ # We test the boundary case of a logging level equal to 19.
+ # In practice, we will probably only be calling log.debug(),
+ # which corresponds to a logging level of 10.
+ level = logging.INFO - 1 # Equals 19.
+ self._log.log(level, "test message")
+ self._assert_log_messages([])
+
+ def test_two_messages(self):
+ self._log.info("message1")
+ self._log.info("message2")
+ self._assert_log_messages(["unittest: [INFO] message1\n",
+ "unittest: [INFO] message2\n"])
+
+
+class ConfigureLoggingCustomLevelTest(ConfigureLoggingTestBase):
+
+ """Tests configure_logging() with a custom logging level."""
+
+ _level = 36
+
+ def _logging_level(self):
+ return self._level
+
+ def test_logged_message(self):
+ self._log.log(self._level, "test message")
+ self._assert_log_messages(["unittest: [Level 36] test message\n"])
+
+ def test_below_threshold_message(self):
+ self._log.log(self._level - 1, "test message")
+ self._assert_log_messages([])
diff --git a/WebKitTools/Scripts/webkitpy/common/system/ospath.py b/WebKitTools/Scripts/webkitpy/common/system/ospath.py
new file mode 100644
index 0000000..aed7a3d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/ospath.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Contains a substitute for Python 2.6's os.path.relpath()."""
+
+import os
+
+
+# This function is a replacement for os.path.relpath(), which is only
+# available in Python 2.6:
+#
+# http://docs.python.org/library/os.path.html#os.path.relpath
+#
+# It should behave essentially the same as os.path.relpath(), except for
+# returning None on paths not contained in abs_start_path.
+def relpath(path, start_path, os_path_abspath=None):
+ """Return a path relative to the given start path, or None.
+
+ Returns None if the path is not contained in the directory start_path.
+
+ Args:
+ path: An absolute or relative path to convert to a relative path.
+ start_path: The path relative to which the given path should be
+ converted.
+ os_path_abspath: A replacement function for unit testing. This
+ function should strip trailing slashes just like
+ os.path.abspath(). Defaults to os.path.abspath.
+
+ """
+ if os_path_abspath is None:
+ os_path_abspath = os.path.abspath
+
+ # Since os_path_abspath() calls os.path.normpath()--
+ #
+ # (see http://docs.python.org/library/os.path.html#os.path.abspath )
+ #
+ # it also removes trailing slashes and converts forward and backward
+ # slashes to the preferred slash os.sep.
+ start_path = os_path_abspath(start_path)
+ path = os_path_abspath(path)
+
+ if not path.lower().startswith(start_path.lower()):
+ # Then path is outside the directory given by start_path.
+ return None
+
+ rel_path = path[len(start_path):]
+
+ if not rel_path:
+ # Then the paths are the same.
+ pass
+ elif rel_path[0] == os.sep:
+ # It is probably sufficient to remove just the first character
+ # since os.path.normpath() collapses separators, but we use
+ # lstrip() just to be sure.
+ rel_path = rel_path.lstrip(os.sep)
+ else:
+ # We are in the case typified by the following example:
+ #
+ # start_path = "/tmp/foo"
+ # path = "/tmp/foobar"
+ # rel_path = "bar"
+ return None
+
+ return rel_path
diff --git a/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py
new file mode 100644
index 0000000..0493c68
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Unit tests for ospath.py."""
+
+import os
+import unittest
+
+from webkitpy.common.system.ospath import relpath
+
+
+# Make sure the tests in this class are platform independent.
+class RelPathTest(unittest.TestCase):
+
+ """Tests relpath()."""
+
+ os_path_abspath = lambda self, path: path
+
+ def _rel_path(self, path, abs_start_path):
+ return relpath(path, abs_start_path, self.os_path_abspath)
+
+ def test_same_path(self):
+ rel_path = self._rel_path("WebKit", "WebKit")
+ self.assertEquals(rel_path, "")
+
+ def test_long_rel_path(self):
+ start_path = "WebKit"
+ expected_rel_path = os.path.join("test", "Foo.txt")
+ path = os.path.join(start_path, expected_rel_path)
+
+ rel_path = self._rel_path(path, start_path)
+ self.assertEquals(expected_rel_path, rel_path)
+
+ def test_none_rel_path(self):
+ """Test _rel_path() with None return value."""
+ start_path = "WebKit"
+ path = os.path.join("other_dir", "foo.txt")
+
+ rel_path = self._rel_path(path, start_path)
+ self.assertTrue(rel_path is None)
+
+ rel_path = self._rel_path("WebKitTools", "WebKit")
+ self.assertTrue(rel_path is None)
diff --git a/WebKitTools/Scripts/webkitpy/outputcapture.py b/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
index 592a669..592a669 100644
--- a/WebKitTools/Scripts/webkitpy/outputcapture.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
diff --git a/WebKitTools/Scripts/webkitpy/user.py b/WebKitTools/Scripts/webkitpy/common/system/user.py
index b2ec19e..076f965 100644
--- a/WebKitTools/Scripts/webkitpy/user.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/user.py
@@ -31,15 +31,34 @@ import shlex
import subprocess
import webbrowser
+try:
+ import readline
+except ImportError:
+ print "Unable to import readline. If you're using MacPorts, try running:"
+ print " sudo port install py25-readline"
+ exit(1)
+
+
class User(object):
- @staticmethod
- def prompt(message, repeat=1, raw_input=raw_input):
+ # FIXME: These are @classmethods because scm.py and bugzilla.py don't have a Tool object (thus no User instance).
+ @classmethod
+ def prompt(cls, message, repeat=1, raw_input=raw_input):
response = None
while (repeat and not response):
repeat -= 1
response = raw_input(message)
return response
+ @classmethod
+ def prompt_with_list(cls, list_title, list_items):
+ print list_title
+ i = 0
+ for item in list_items:
+ i += 1
+ print "%2d. %s" % (i, item)
+ result = int(cls.prompt("Enter a number: ")) - 1
+ return list_items[result]
+
def edit(self, files):
editor = os.environ.get("EDITOR") or "vi"
args = shlex.split(editor)
diff --git a/WebKitTools/Scripts/webkitpy/user_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
index 34d9983..dadead3 100644
--- a/WebKitTools/Scripts/webkitpy/user_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
@@ -27,7 +27,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
-from webkitpy.user import User
+
+from webkitpy.common.system.user import User
class UserTest(unittest.TestCase):
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/__init__.py b/WebKitTools/Scripts/webkitpy/common/thread/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/thread/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py b/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py
new file mode 100644
index 0000000..0e39285
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py
@@ -0,0 +1,59 @@
+# Copyright (c) 2010 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.
+
+
+class MessagePumpDelegate(object):
+ def schedule(self, interval, callback):
+ raise NotImplementedError, "subclasses must implement"
+
+ def message_available(self, message):
+ raise NotImplementedError, "subclasses must implement"
+
+ def final_message_delivered(self):
+ raise NotImplementedError, "subclasses must implement"
+
+
+class MessagePump(object):
+ interval = 10 # seconds
+
+ def __init__(self, delegate, message_queue):
+ self._delegate = delegate
+ self._message_queue = message_queue
+ self._schedule()
+
+ def _schedule(self):
+ self._delegate.schedule(self.interval, self._callback)
+
+ def _callback(self):
+ (messages, is_running) = self._message_queue.take_all()
+ for message in messages:
+ self._delegate.message_available(message)
+ if not is_running:
+ self._delegate.final_message_delivered()
+ return
+ self._schedule()
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py b/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py
new file mode 100644
index 0000000..f731db2
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
+from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
+
+
+class TestDelegate(MessagePumpDelegate):
+ def __init__(self):
+ self.log = []
+
+ def schedule(self, interval, callback):
+ self.callback = callback
+ self.log.append("schedule")
+
+ def message_available(self, message):
+ self.log.append("message_available: %s" % message)
+
+ def final_message_delivered(self):
+ self.log.append("final_message_delivered")
+
+
+class MessagePumpTest(unittest.TestCase):
+
+ def test_basic(self):
+ queue = ThreadedMessageQueue()
+ delegate = TestDelegate()
+ pump = MessagePump(delegate, queue)
+ self.assertEqual(delegate.log, [
+ 'schedule'
+ ])
+ delegate.callback()
+ queue.post("Hello")
+ queue.post("There")
+ delegate.callback()
+ self.assertEqual(delegate.log, [
+ 'schedule',
+ 'schedule',
+ 'message_available: Hello',
+ 'message_available: There',
+ 'schedule'
+ ])
+ queue.post("More")
+ queue.post("Messages")
+ queue.stop()
+ delegate.callback()
+ self.assertEqual(delegate.log, [
+ 'schedule',
+ 'schedule',
+ 'message_available: Hello',
+ 'message_available: There',
+ 'schedule',
+ 'message_available: More',
+ 'message_available: Messages',
+ 'final_message_delivered'
+ ])
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py
index 0ea8053..6cb6f8c 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/util.py
+++ b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py
@@ -1,5 +1,4 @@
-# Copyright 2009, Google Inc.
-# All rights reserved.
+# Copyright (c) 2010 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
@@ -27,33 +26,30 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import threading
-"""Web Sockets utilities.
-"""
+class ThreadedMessageQueue(object):
+ def __init__(self):
+ self._messages = []
+ self._is_running = True
+ self._lock = threading.Lock()
-import StringIO
-import traceback
+ def post(self, message):
+ self._lock.acquire()
+ self._messages.append(message)
+ self._lock.release()
+ def stop(self):
+ self._lock.acquire()
+ self._is_running = False
+ self._lock.release()
-def get_stack_trace():
- """Get the current stack trace as string.
+ def take_all(self):
+ self._lock.acquire()
+ messages = self._messages
+ is_running = self._is_running
+ self._messages = []
+ self._lock.release()
+ return (messages, is_running)
- 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()
-
-
-def prepend_message_to_exception(message, exc):
- """Prepend message to the exception."""
-
- exc.args = (message + str(exc),)
- return
-
-
-# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py
new file mode 100644
index 0000000..cb67c1e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
+
+class ThreadedMessageQueueTest(unittest.TestCase):
+
+ def test_basic(self):
+ queue = ThreadedMessageQueue()
+ queue.post("Hello")
+ queue.post("There")
+ (messages, is_running) = queue.take_all()
+ self.assertEqual(messages, ["Hello", "There"])
+ self.assertTrue(is_running)
+ (messages, is_running) = queue.take_all()
+ self.assertEqual(messages, [])
+ self.assertTrue(is_running)
+ queue.post("More")
+ queue.stop()
+ queue.post("Messages")
+ (messages, is_running) = queue.take_all()
+ self.assertEqual(messages, ["More", "Messages"])
+ self.assertFalse(is_running)
+ (messages, is_running) = queue.take_all()
+ self.assertEqual(messages, [])
+ self.assertFalse(is_running)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/driver_test.py b/WebKitTools/Scripts/webkitpy/layout_tests/driver_test.py
index 6e4ba99..231ed70 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/driver_test.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/driver_test.py
@@ -61,17 +61,19 @@ def run_tests(port, options, tests):
if __name__ == '__main__':
- optparser = optparse.OptionParser()
- optparser.add_option('-p', '--platform', action='store', default='mac',
- help='Platform to test (e.g., "mac", "chromium-mac", etc.')
- optparser.add_option('-t', '--target', action='store', default='Release',
- help='build type ("Debug" or "Release")')
- optparser.add_option('', '--timeout', action='store', default='2000',
- help='test timeout in milliseconds (2000 by default)')
- optparser.add_option('', '--wrapper', action='store')
- optparser.add_option('', '--no-pixel-tests', action='store_true',
- default=False,
- help='disable pixel-to-pixel PNG comparisons')
+ # FIXME: configuration_options belong in a shared location.
+ configuration_options = [
+ optparse.make_option('--debug', action='store_const', const='Debug', dest="configuration", help='Set the configuration to Debug'),
+ optparse.make_option('--release', action='store_const', const='Release', dest="configuration", help='Set the configuration to Release'),
+ ]
+ misc_options = [
+ optparse.make_option('-p', '--platform', action='store', default='mac', help='Platform to test (e.g., "mac", "chromium-mac", etc.'),
+ optparse.make_option('--timeout', action='store', default='2000', help='test timeout in milliseconds (2000 by default)'),
+ optparse.make_option('--wrapper', action='store'),
+ optparse.make_option('--no-pixel-tests', action='store_true', default=False, help='disable pixel-to-pixel PNG comparisons'),
+ ]
+ option_list = configuration_options + misc_options
+ optparser = optparse.OptionParser(option_list=option_list)
options, args = optparser.parse_args()
p = port.get(options.platform, options)
run_tests(p, options, args)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
index 3452035..e61d11f 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_shell_thread.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
@@ -27,10 +27,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""A Thread object for running the test shell and processing URLs from a
+"""A Thread object for running DumpRenderTree and processing URLs from a
shared queue.
-Each thread runs a separate instance of the test_shell binary and validates
+Each thread runs a separate instance of the DumpRenderTree binary and validates
the output. When there are no more URLs to process in the shared queue, the
thread exits.
"""
@@ -47,23 +47,26 @@ import time
import test_failures
+_log = logging.getLogger("webkitpy.layout_tests.layout_package."
+ "dump_render_tree_thread")
-def process_output(port, test_info, test_types, test_args, target, output_dir,
- crash, timeout, test_run_time, actual_checksum,
+
+def process_output(port, test_info, test_types, test_args, configuration,
+ output_dir, crash, timeout, test_run_time, actual_checksum,
output, error):
- """Receives the output from a test_shell process, subjects it to a number
- of tests, and returns a list of failure types the test produced.
+ """Receives the output from a DumpRenderTree process, subjects it to a
+ number of tests, and returns a list of failure types the test produced.
Args:
port: port-specific hooks
- proc: an active test_shell process
+ proc: an active DumpRenderTree process
test_info: Object containing the test filename, uri and timeout
test_types: list of test types to subject the output to
test_args: arguments to be passed to each test
- target: Debug or Release
+ configuration: Debug or Release
output_dir: directory to put crash stack traces into
- Returns: a list of failure objects and times for the test being processed
+ Returns: a TestResult object
"""
failures = []
@@ -79,16 +82,17 @@ def process_output(port, test_info, test_types, test_args, target, output_dir,
failures.append(test_failures.FailureTimeout())
if crash:
- logging.debug("Stacktrace for %s:\n%s" % (test_info.filename, error))
+ _log.debug("Stacktrace for %s:\n%s" % (test_info.filename, error))
# Strip off "file://" since RelativeTestFilename expects
# filesystem paths.
- filename = os.path.join(output_dir, test_info.filename)
+ filename = os.path.join(output_dir, port.relative_test_filename(
+ test_info.filename))
filename = os.path.splitext(filename)[0] + "-stack.txt"
port.maybe_make_directory(os.path.split(filename)[0])
- open(filename, "wb").write(error)
+ open(filename, "wb").write(error) # FIXME: This leaks a file handle.
elif error:
- logging.debug("Previous test output extra lines after dump:\n%s" %
- error)
+ _log.debug("Previous test output extra lines after dump:\n%s" %
+ error)
# Check the output and save the results.
start_time = time.time()
@@ -97,7 +101,7 @@ def process_output(port, test_info, test_types, test_args, target, output_dir,
start_diff_time = time.time()
new_failures = test_type.compare_output(port, test_info.filename,
output, local_test_args,
- target)
+ configuration)
# Don't add any more failures if we already have a crash, so we don't
# double-report those tests. We do double-report for timeouts since
# we still want to see the text and image output.
@@ -107,26 +111,27 @@ def process_output(port, test_info, test_types, test_args, target, output_dir,
time.time() - start_diff_time)
total_time_for_all_diffs = time.time() - start_diff_time
- return TestStats(test_info.filename, failures, test_run_time,
- total_time_for_all_diffs, time_for_diffs)
+ return TestResult(test_info.filename, failures, test_run_time,
+ total_time_for_all_diffs, time_for_diffs)
-class TestStats:
+class TestResult(object):
def __init__(self, filename, failures, test_run_time,
total_time_for_all_diffs, time_for_diffs):
- self.filename = filename
self.failures = failures
+ self.filename = filename
self.test_run_time = test_run_time
- self.total_time_for_all_diffs = total_time_for_all_diffs
self.time_for_diffs = time_for_diffs
+ self.total_time_for_all_diffs = total_time_for_all_diffs
+ self.type = test_failures.determine_result_type(failures)
class SingleTestThread(threading.Thread):
"""Thread wrapper for running a single test file."""
def __init__(self, port, image_path, shell_args, test_info,
- test_types, test_args, target, output_dir):
+ test_types, test_args, configuration, output_dir):
"""
Args:
port: object implementing port-specific hooks
@@ -142,32 +147,32 @@ class SingleTestThread(threading.Thread):
self._test_info = test_info
self._test_types = test_types
self._test_args = test_args
- self._target = target
+ self._configuration = configuration
self._output_dir = output_dir
def run(self):
- driver = self._port.start_test_driver(self._image_path,
- self._shell_args)
+ test_info = self._test_info
+ driver = self._port.start_driver(self._image_path, self._shell_args)
start = time.time()
crash, timeout, actual_checksum, output, error = \
driver.run_test(test_info.uri.strip(), test_info.timeout,
- test_info.image_hash)
+ test_info.image_hash())
end = time.time()
- self._test_stats = process_output(self._port,
- self._test_info, self._test_types, self._test_args,
- self._target, self._output_dir, crash, timeout, end - start,
+ self._test_result = process_output(self._port,
+ test_info, self._test_types, self._test_args,
+ self._configuration, self._output_dir, crash, timeout, end - start,
actual_checksum, output, error)
driver.stop()
- def get_test_stats(self):
- return self._test_stats
+ def get_test_result(self):
+ return self._test_result
class TestShellThread(threading.Thread):
def __init__(self, port, filename_list_queue, result_queue,
test_types, test_args, image_path, shell_args, options):
- """Initialize all the local state for this test shell thread.
+ """Initialize all the local state for this DumpRenderTree thread.
Args:
port: interface to port-specific hooks
@@ -178,7 +183,7 @@ class TestShellThread(threading.Thread):
test_types: A list of TestType objects to run the test output
against.
test_args: A TestArguments object to pass to each TestType.
- shell_args: Any extra arguments to be passed to test_shell.exe.
+ shell_args: Any extra arguments to be passed to DumpRenderTree.
options: A property dictionary as produced by optparse. The
command-line options should match those expected by
run_webkit_tests; they are typically passed via the
@@ -197,7 +202,7 @@ class TestShellThread(threading.Thread):
self._canceled = False
self._exception_info = None
self._directory_timing_stats = {}
- self._test_stats = []
+ self._test_results = []
self._num_tests = 0
self._start_time = 0
self._stop_time = 0
@@ -214,10 +219,13 @@ class TestShellThread(threading.Thread):
(number of tests in that directory, time to run the tests)"""
return self._directory_timing_stats
- def get_individual_test_stats(self):
- """Returns a list of (test_filename, time_to_run_test,
- total_time_for_all_diffs, time_for_diffs) tuples."""
- return self._test_stats
+ def get_test_results(self):
+ """Return the list of all tests run on this thread.
+
+ This is used to calculate per-thread statistics.
+
+ """
+ return self._test_results
def cancel(self):
"""Set a flag telling this thread to quit."""
@@ -242,17 +250,17 @@ class TestShellThread(threading.Thread):
self._start_time = time.time()
self._num_tests = 0
try:
- logging.debug('%s starting' % (self.getName()))
+ _log.debug('%s starting' % (self.getName()))
self._run(test_runner=None, result_summary=None)
- logging.debug('%s done (%d tests)' % (self.getName(),
- self.get_num_tests()))
+ _log.debug('%s done (%d tests)' % (self.getName(),
+ self.get_num_tests()))
except:
# Save the exception for our caller to see.
self._exception_info = sys.exc_info()
self._stop_time = time.time()
# Re-raise it and die.
- logging.error('%s dying: %s' % (self.getName(),
- self._exception_info))
+ _log.error('%s dying: %s' % (self.getName(),
+ self._exception_info))
raise
self._stop_time = time.time()
@@ -275,8 +283,8 @@ class TestShellThread(threading.Thread):
try:
batch_size = int(self._options.batch_size)
except:
- logging.info("Ignoring invalid batch size '%s'" %
- self._options.batch_size)
+ _log.info("Ignoring invalid batch size '%s'" %
+ self._options.batch_size)
# Append tests we're running to the existing tests_run.txt file.
# This is created in run_webkit_tests.py:_PrepareListsAndPrintOutput.
@@ -286,7 +294,7 @@ class TestShellThread(threading.Thread):
while True:
if self._canceled:
- logging.info('Testing canceled')
+ _log.info('Testing canceled')
tests_run_file.close()
return
@@ -300,7 +308,7 @@ class TestShellThread(threading.Thread):
self._current_dir, self._filename_list = \
self._filename_list_queue.get_nowait()
except Queue.Empty:
- self._kill_test_shell()
+ self._kill_dump_render_tree()
tests_run_file.close()
return
@@ -313,31 +321,33 @@ class TestShellThread(threading.Thread):
batch_count += 1
self._num_tests += 1
if self._options.run_singly:
- failures = self._run_test_singly(test_info)
+ result = self._run_test_singly(test_info)
else:
- failures = self._run_test(test_info)
+ result = self._run_test(test_info)
filename = test_info.filename
tests_run_file.write(filename + "\n")
- if failures:
- # Check and kill test shell if we need too.
- if len([1 for f in failures if f.should_kill_test_shell()]):
- self._kill_test_shell()
+ if result.failures:
+ # Check and kill DumpRenderTree if we need to.
+ if len([1 for f in result.failures
+ if f.should_kill_dump_render_tree()]):
+ self._kill_dump_render_tree()
# Reset the batch count since the shell just bounced.
batch_count = 0
# Print the error message(s).
- error_str = '\n'.join([' ' + f.message() for f in failures])
- logging.debug("%s %s failed:\n%s" % (self.getName(),
- self._port.relative_test_filename(filename),
- error_str))
+ error_str = '\n'.join([' ' + f.message() for
+ f in result.failures])
+ _log.debug("%s %s failed:\n%s" % (self.getName(),
+ self._port.relative_test_filename(filename),
+ error_str))
else:
- logging.debug("%s %s passed" % (self.getName(),
- self._port.relative_test_filename(filename)))
- self._result_queue.put((filename, failures))
+ _log.debug("%s %s passed" % (self.getName(),
+ self._port.relative_test_filename(filename)))
+ self._result_queue.put(result)
if batch_size > 0 and batch_count > batch_size:
# Bounce the shell and reset count.
- self._kill_test_shell()
+ self._kill_dump_render_tree()
batch_count = 0
if test_runner:
@@ -353,61 +363,64 @@ class TestShellThread(threading.Thread):
Args:
test_info: Object containing the test filename, uri and timeout
- Return:
- A list of TestFailure objects describing the error.
+ Returns:
+ A TestResult
+
"""
worker = SingleTestThread(self._port, self._image_path,
self._shell_args,
test_info,
self._test_types,
self._test_args,
- self._options.target,
+ self._options.configuration,
self._options.results_directory)
worker.start()
- # When we're running one test per test_shell process, we can enforce
- # a hard timeout. the test_shell watchdog uses 2.5x the timeout
- # We want to be larger than that.
+ # When we're running one test per DumpRenderTree process, we can
+ # enforce a hard timeout. The DumpRenderTree watchdog uses 2.5x
+ # the timeout; we want to be larger than that.
worker.join(int(test_info.timeout) * 3.0 / 1000.0)
if worker.isAlive():
# If join() returned with the thread still running, the
- # test_shell.exe is completely hung and there's nothing
+ # DumpRenderTree is completely hung and there's nothing
# more we can do with it. We have to kill all the
- # test_shells to free it up. If we're running more than
- # one test_shell thread, we'll end up killing the other
- # test_shells too, introducing spurious crashes. We accept that
- # tradeoff in order to avoid losing the rest of this thread's
- # results.
- logging.error('Test thread hung: killing all test_shells')
+ # DumpRenderTrees to free it up. If we're running more than
+ # one DumpRenderTree thread, we'll end up killing the other
+ # DumpRenderTrees too, introducing spurious crashes. We accept
+ # that tradeoff in order to avoid losing the rest of this
+ # thread's results.
+ _log.error('Test thread hung: killing all DumpRenderTrees')
worker._driver.stop()
try:
- stats = worker.get_test_stats()
- self._test_stats.append(stats)
- failures = stats.failures
+ result = worker.get_test_result()
except AttributeError, e:
failures = []
- logging.error('Cannot get results of test: %s' %
- test_info.filename)
+ _log.error('Cannot get results of test: %s' %
+ test_info.filename)
+ result = TestResult(test_info.filename, failures=[],
+ test_run_time=0, total_time_for_all_diffs=0,
+ time_for_diffs=0)
- return failures
+ return result
def _run_test(self, test_info):
- """Run a single test file using a shared test_shell process.
+ """Run a single test file using a shared DumpRenderTree process.
Args:
test_info: Object containing the test filename, uri and timeout
- Return:
+ Returns:
A list of TestFailure objects describing the error.
+
"""
- self._ensure_test_shell_is_running()
+ self._ensure_dump_render_tree_is_running()
# The pixel_hash is used to avoid doing an image dump if the
# checksums match, so it should be set to a blank value if we
# are generating a new baseline. (Otherwise, an image from a
# previous run will be copied into the baseline.)
- image_hash = test_info.image_hash
+ image_hash = test_info.image_hash()
if image_hash and self._test_args.new_baseline:
image_hash = ""
start = time.time()
@@ -415,26 +428,27 @@ class TestShellThread(threading.Thread):
self._driver.run_test(test_info.uri, test_info.timeout, image_hash)
end = time.time()
- stats = process_output(self._port, test_info, self._test_types,
- self._test_args, self._options.target,
- self._options.results_directory, crash,
- timeout, end - start, actual_checksum,
- output, error)
+ result = process_output(self._port, test_info, self._test_types,
+ self._test_args, self._options.configuration,
+ self._options.results_directory, crash,
+ timeout, end - start, actual_checksum,
+ output, error)
+ self._test_results.append(result)
+ return result
+
+ def _ensure_dump_render_tree_is_running(self):
+ """Start the shared DumpRenderTree, if it's not running.
- self._test_stats.append(stats)
- return stats.failures
+ This is not for use when running tests singly, since those each start
+ a separate DumpRenderTree in their own thread.
- def _ensure_test_shell_is_running(self):
- """Start the shared test shell, if it's not running. Not for use when
- running tests singly, since those each start a separate test shell in
- their own thread.
"""
if (not self._driver or self._driver.poll() is not None):
self._driver = self._port.start_driver(
self._image_path, self._shell_args)
- def _kill_test_shell(self):
- """Kill the test shell process if it's running."""
+ def _kill_dump_render_tree(self):
+ """Kill the DumpRenderTree process if it's running."""
if self._driver:
self._driver.stop()
self._driver = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
index 520ab1f..cee44ad 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
@@ -29,12 +29,11 @@
import logging
import os
-import simplejson
-
-from layout_package import json_results_generator
-from layout_package import test_expectations
-from layout_package import test_failures
+from webkitpy.layout_tests.layout_package import json_results_generator
+from webkitpy.layout_tests.layout_package import test_expectations
+from webkitpy.layout_tests.layout_package import test_failures
+import webkitpy.thirdparty.simplejson as simplejson
class JSONLayoutResultsGenerator(json_results_generator.JSONResultsGenerator):
"""A JSON results generator for layout tests."""
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
index 84be0e1..6263540 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
@@ -29,14 +29,17 @@
import logging
import os
-import simplejson
import subprocess
import sys
import time
import urllib2
import xml.dom.minidom
-from layout_package import test_expectations
+from webkitpy.layout_tests.layout_package import test_expectations
+import webkitpy.thirdparty.simplejson as simplejson
+
+_log = logging.getLogger("webkitpy.layout_tests.layout_package."
+ "json_results_generator")
class JSONResultsGenerator(object):
@@ -154,8 +157,8 @@ class JSONResultsGenerator(object):
# Check if we have the archived JSON file on the buildbot server.
results_file_url = (self._builder_base_url +
self._build_name + "/" + self.RESULTS_FILENAME)
- logging.error("Local results.json file does not exist. Grabbing "
- "it off the archive at " + results_file_url)
+ _log.error("Local results.json file does not exist. Grabbing "
+ "it off the archive at " + results_file_url)
try:
results_file = urllib2.urlopen(results_file_url)
@@ -177,11 +180,11 @@ class JSONResultsGenerator(object):
try:
results_json = simplejson.loads(old_results)
except:
- logging.debug("results.json was not valid JSON. Clobbering.")
+ _log.debug("results.json was not valid JSON. Clobbering.")
# The JSON file is not valid JSON. Just clobber the results.
results_json = {}
else:
- logging.debug('Old JSON results do not exist. Starting fresh.')
+ _log.debug('Old JSON results do not exist. Starting fresh.')
results_json = {}
return results_json, error
@@ -192,14 +195,14 @@ class JSONResultsGenerator(object):
if error:
# If there was an error don't write a results.json
# file at all as it would lose all the information on the bot.
- logging.error("Archive directory is inaccessible. Not modifying "
- "or clobbering the results.json file: " + str(error))
+ _log.error("Archive directory is inaccessible. Not modifying "
+ "or clobbering the results.json file: " + str(error))
return None
builder_name = self._builder_name
if results_json and builder_name not in results_json:
- logging.debug("Builder name (%s) is not in the results.json file."
- % builder_name)
+ _log.debug("Builder name (%s) is not in the results.json file."
+ % builder_name)
self._convert_json_to_current_version(results_json)
@@ -307,16 +310,20 @@ class JSONResultsGenerator(object):
# These next two branches test to see which source repos we can
# pull revisions from.
if hasattr(self._port, 'path_from_webkit_base'):
- path_to_webkit = self._port.path_from_webkit_base()
+ path_to_webkit = self._port.path_from_webkit_base('WebCore')
self._insert_item_into_raw_list(results_for_builder,
self._get_svn_revision(path_to_webkit),
self.WEBKIT_SVN)
if hasattr(self._port, 'path_from_chromium_base'):
- path_to_chrome = self._port.path_from_chromium_base()
- self._insert_item_into_raw_list(results_for_builder,
- self._get_svn_revision(path_to_chrome),
- self.CHROME_SVN)
+ try:
+ path_to_chrome = self._port.path_from_chromium_base()
+ self._insert_item_into_raw_list(results_for_builder,
+ self._get_svn_revision(path_to_chrome),
+ self.CHROME_SVN)
+ except AssertionError:
+ # We're not in a Chromium checkout, that's ok.
+ pass
self._insert_item_into_raw_list(results_for_builder,
int(time.time()),
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py
index 72b30a1..930b9e4 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py
@@ -34,6 +34,10 @@ and rewritten repeatedly, without producing multiple lines of output. It
can be used to produce effects like progress bars.
"""
+import logging
+
+_log = logging.getLogger("webkitpy.layout_tests.metered_stream")
+
class MeteredStream:
"""This class is a wrapper around a stream that allows you to implement
@@ -57,8 +61,7 @@ class MeteredStream:
self._last_update = ""
def write(self, txt):
- """Write text directly to the stream, overwriting and resetting the
- meter."""
+ """Write to the stream, overwriting and resetting the meter."""
if self._dirty:
self.update("")
self._dirty = False
@@ -68,22 +71,43 @@ class MeteredStream:
"""Flush any buffered output."""
self._stream.flush()
- def update(self, str):
- """Write an update to the stream that will get overwritten by the next
- update() or by a write().
+ def progress(self, str):
+ """
+ Write a message to the stream that will get overwritten.
This is used for progress updates that don't need to be preserved in
- the log. Note that verbose disables this routine; we have this in
- case we are logging lots of output and the update()s will get lost
- or won't work properly (typically because verbose streams are
- redirected to files.
-
- TODO(dpranke): figure out if there is a way to detect if we're writing
- to a stream that handles CRs correctly (e.g., terminals). That might
- be a cleaner way of handling this.
+ the log. If the MeteredStream was initialized with verbose==True,
+ then this output is discarded. We have this in case we are logging
+ lots of output and the update()s will get lost or won't work
+ properly (typically because verbose streams are redirected to files).
+
"""
if self._verbose:
return
+ self._write(str)
+
+ def update(self, str):
+ """
+ Write a message that is also included when logging verbosely.
+
+ This routine preserves the same console logging behavior as progress(),
+ but will also log the message if verbose() was true.
+
+ """
+ # Note this is a separate routine that calls either into the logger
+ # or the metering stream. We have to be careful to avoid a layering
+ # inversion (stream calling back into the logger).
+ if self._verbose:
+ _log.info(str)
+ else:
+ self._write(str)
+
+ def _write(self, str):
+ """Actually write the message to the stream."""
+
+ # FIXME: Figure out if there is a way to detect if we're writing
+ # to a stream that handles CRs correctly (e.g., terminals). That might
+ # be a cleaner way of handling this.
# Print the necessary number of backspaces to erase the previous
# message.
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
index 01add62..38223dd 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
@@ -36,7 +36,10 @@ import os
import re
import sys
-import simplejson
+import webkitpy.thirdparty.simplejson as simplejson
+
+_log = logging.getLogger("webkitpy.layout_tests.layout_package."
+ "test_expectations")
# Test expectation and modifier constants.
(PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, TIMEOUT, CRASH, SKIP, WONTFIX,
@@ -46,11 +49,46 @@ import simplejson
(NO_CHANGE, REMOVE_TEST, REMOVE_PLATFORM, ADD_PLATFORMS_EXCEPT_THIS) = range(4)
+def result_was_expected(result, expected_results, test_needs_rebaselining,
+ test_is_skipped):
+ """Returns whether we got a result we were expecting.
+ Args:
+ result: actual result of a test execution
+ expected_results: set of results listed in test_expectations
+ test_needs_rebaselining: whether test was marked as REBASELINE
+ test_is_skipped: whether test was marked as SKIP"""
+ if result in expected_results:
+ return True
+ if result in (IMAGE, TEXT, IMAGE_PLUS_TEXT) and FAIL in expected_results:
+ return True
+ if result == MISSING and test_needs_rebaselining:
+ return True
+ if result == SKIP and test_is_skipped:
+ return True
+ return False
+
+
+def remove_pixel_failures(expected_results):
+ """Returns a copy of the expected results for a test, except that we
+ drop any pixel failures and return the remaining expectations. For example,
+ if we're not running pixel tests, then tests expected to fail as IMAGE
+ will PASS."""
+ expected_results = expected_results.copy()
+ if IMAGE in expected_results:
+ expected_results.remove(IMAGE)
+ expected_results.add(PASS)
+ if IMAGE_PLUS_TEXT in expected_results:
+ expected_results.remove(IMAGE_PLUS_TEXT)
+ expected_results.add(TEXT)
+ return expected_results
+
+
class TestExpectations:
TEST_LIST = "test_expectations.txt"
def __init__(self, port, tests, expectations, test_platform_name,
- is_debug_mode, is_lint_mode, tests_are_present=True):
+ is_debug_mode, is_lint_mode, tests_are_present=True,
+ overrides=None):
"""Loads and parses the test expectations given in the string.
Args:
port: handle to object containing platform-specific functionality
@@ -67,10 +105,14 @@ class TestExpectations:
system and can be probed for. This is useful for distinguishing
test files from directories, and is needed by the LTTF
dashboard, where the files aren't actually locally present.
+ overrides: test expectations that are allowed to override any
+ entries in |expectations|. This is used by callers
+ that need to manage two sets of expectations (e.g., upstream
+ and downstream expectations).
"""
self._expected_failures = TestExpectationsFile(port, expectations,
tests, test_platform_name, is_debug_mode, is_lint_mode,
- tests_are_present=tests_are_present)
+ tests_are_present=tests_are_present, overrides=overrides)
# TODO(ojan): Allow for removing skipped tests when getting the list of
# tests to run, but not when getting metrics.
@@ -101,12 +143,16 @@ class TestExpectations:
retval = []
for expectation in expectations:
- for item in TestExpectationsFile.EXPECTATIONS.items():
- if item[1] == expectation:
- retval.append(item[0])
- break
+ retval.append(self.expectation_to_string(expectation))
- return " ".join(retval).upper()
+ return " ".join(retval)
+
+ def expectation_to_string(self, expectation):
+ """Return the uppercased string equivalent of a given expectation."""
+ for item in TestExpectationsFile.EXPECTATIONS.items():
+ if item[1] == expectation:
+ return item[0].upper()
+ return ""
def get_timeline_for_test(self, test):
return self._expected_failures.get_timeline_for_test(test)
@@ -117,14 +163,13 @@ class TestExpectations:
def get_tests_with_timeline(self, timeline):
return self._expected_failures.get_tests_with_timeline(timeline)
- def matches_an_expected_result(self, test, result):
- """Returns whether we got one of the expected results for this test."""
- return (result in self._expected_failures.get_expectations(test) or
- (result in (IMAGE, TEXT, IMAGE_PLUS_TEXT) and
- FAIL in self._expected_failures.get_expectations(test)) or
- result == MISSING and self.is_rebaselining(test) or
- result == SKIP and self._expected_failures.has_modifier(test,
- SKIP))
+ def matches_an_expected_result(self, test, result,
+ pixel_tests_are_enabled):
+ expected_results = self._expected_failures.get_expectations(test)
+ if not pixel_tests_are_enabled:
+ expected_results = remove_pixel_failures(expected_results)
+ return result_was_expected(result, expected_results,
+ self.is_rebaselining(test), self.has_modifier(test, SKIP))
def is_rebaselining(self, test):
return self._expected_failures.has_modifier(test, REBASELINE)
@@ -232,8 +277,8 @@ class TestExpectationsFile:
IMAGE: ('image mismatch', 'image mismatch'),
IMAGE_PLUS_TEXT: ('image and text mismatch',
'image and text mismatch'),
- CRASH: ('test shell crash',
- 'test shell crashes'),
+ CRASH: ('DumpRenderTree crash',
+ 'DumpRenderTree crashes'),
TIMEOUT: ('test timed out', 'tests timed out'),
MISSING: ('no expected result found',
'no expected results found')}
@@ -261,7 +306,7 @@ class TestExpectationsFile:
def __init__(self, port, expectations, full_test_list, test_platform_name,
is_debug_mode, is_lint_mode, suppress_errors=False,
- tests_are_present=True):
+ tests_are_present=True, overrides=None):
"""
expectations: Contents of the expectations file
full_test_list: The list of all tests to be run pending processing of
@@ -275,6 +320,10 @@ class TestExpectationsFile:
tests_are_present: Whether the test files are present in the local
filesystem. The LTTF Dashboard uses False here to avoid having to
keep a local copy of the tree.
+ overrides: test expectations that are allowed to override any
+ entries in |expectations|. This is used by callers
+ that need to manage two sets of expectations (e.g., upstream
+ and downstream expectations).
"""
self._port = port
@@ -284,6 +333,7 @@ class TestExpectationsFile:
self._is_debug_mode = is_debug_mode
self._is_lint_mode = is_lint_mode
self._tests_are_present = tests_are_present
+ self._overrides = overrides
self._suppress_errors = suppress_errors
self._errors = []
self._non_fatal_errors = []
@@ -311,7 +361,50 @@ class TestExpectationsFile:
self._timeline_to_tests = self._dict_of_sets(self.TIMELINES)
self._result_type_to_tests = self._dict_of_sets(self.RESULT_TYPES)
- self._read(self._get_iterable_expectations())
+ self._read(self._get_iterable_expectations(self._expectations),
+ overrides_allowed=False)
+
+ # List of tests that are in the overrides file (used for checking for
+ # duplicates inside the overrides file itself). Note that just because
+ # a test is in this set doesn't mean it's necessarily overridding a
+ # expectation in the regular expectations; the test might not be
+ # mentioned in the regular expectations file at all.
+ self._overridding_tests = set()
+
+ if overrides:
+ self._read(self._get_iterable_expectations(self._overrides),
+ overrides_allowed=True)
+
+ self._handle_any_read_errors()
+ self._process_tests_without_expectations()
+
+ def _handle_any_read_errors(self):
+ if not self._suppress_errors and (
+ len(self._errors) or len(self._non_fatal_errors)):
+ if self._is_debug_mode:
+ build_type = 'DEBUG'
+ else:
+ build_type = 'RELEASE'
+ _log.error('')
+ _log.error("FAILURES FOR PLATFORM: %s, BUILD_TYPE: %s" %
+ (self._test_platform_name.upper(), build_type))
+
+ for error in self._non_fatal_errors:
+ _log.error(error)
+ _log.error('')
+
+ if len(self._errors):
+ raise SyntaxError('\n'.join(map(str, self._errors)))
+
+ def _process_tests_without_expectations(self):
+ expectations = set([PASS])
+ options = []
+ modifiers = []
+ if self._full_test_list:
+ for test in self._full_test_list:
+ if not test in self._test_list_paths:
+ self._add_test(test, modifiers, expectations, options,
+ overrides_allowed=False)
def _dict_of_sets(self, strings_to_constants):
"""Takes a dict of strings->constants and returns a dict mapping
@@ -321,12 +414,11 @@ class TestExpectationsFile:
d[c] = set()
return d
- def _get_iterable_expectations(self):
+ def _get_iterable_expectations(self, expectations_str):
"""Returns an object that can be iterated over. Allows for not caring
about whether we're iterating over a file or a new-line separated
string."""
- iterable = [x + "\n" for x in
- self._expectations.split("\n")]
+ iterable = [x + "\n" for x in expectations_str.split("\n")]
# Strip final entry if it's empty to avoid added in an extra
# newline.
if iterable[-1] == "\n":
@@ -388,7 +480,7 @@ class TestExpectationsFile:
the updated string.
"""
- f_orig = self._get_iterable_expectations()
+ f_orig = self._get_iterable_expectations(self._expectations)
f_new = []
tests_removed = 0
@@ -400,20 +492,20 @@ class TestExpectationsFile:
platform)
if action == NO_CHANGE:
# Save the original line back to the file
- logging.debug('No change to test: %s', line)
+ _log.debug('No change to test: %s', line)
f_new.append(line)
elif action == REMOVE_TEST:
tests_removed += 1
- logging.info('Test removed: %s', line)
+ _log.info('Test removed: %s', line)
elif action == REMOVE_PLATFORM:
parts = line.split(':')
new_options = parts[0].replace(platform.upper() + ' ', '', 1)
new_line = ('%s:%s' % (new_options, parts[1]))
f_new.append(new_line)
tests_updated += 1
- logging.info('Test updated: ')
- logging.info(' old: %s', line)
- logging.info(' new: %s', new_line)
+ _log.info('Test updated: ')
+ _log.info(' old: %s', line)
+ _log.info(' new: %s', new_line)
elif action == ADD_PLATFORMS_EXCEPT_THIS:
parts = line.split(':')
new_options = parts[0]
@@ -430,15 +522,15 @@ class TestExpectationsFile:
new_line = ('%s:%s' % (new_options, parts[1]))
f_new.append(new_line)
tests_updated += 1
- logging.info('Test updated: ')
- logging.info(' old: %s', line)
- logging.info(' new: %s', new_line)
+ _log.info('Test updated: ')
+ _log.info(' old: %s', line)
+ _log.info(' new: %s', new_line)
else:
- logging.error('Unknown update action: %d; line: %s',
- action, line)
+ _log.error('Unknown update action: %d; line: %s',
+ action, line)
- logging.info('Total tests removed: %d', tests_removed)
- logging.info('Total tests updated: %d', tests_updated)
+ _log.info('Total tests removed: %d', tests_removed)
+ _log.info('Total tests updated: %d', tests_updated)
return "".join(f_new)
@@ -574,7 +666,7 @@ class TestExpectationsFile:
self._all_expectations[test].append(
ModifiersAndExpectations(options, expectations))
- def _read(self, expectations):
+ def _read(self, expectations, overrides_allowed):
"""For each test in an expectations iterable, generate the
expectations for it."""
lineno = 0
@@ -625,30 +717,7 @@ class TestExpectationsFile:
tests = self._expand_tests(test_list_path)
self._add_tests(tests, expectations, test_list_path, lineno,
- modifiers, options)
-
- if not self._suppress_errors and (
- len(self._errors) or len(self._non_fatal_errors)):
- if self._is_debug_mode:
- build_type = 'DEBUG'
- else:
- build_type = 'RELEASE'
- print "\nFAILURES FOR PLATFORM: %s, BUILD_TYPE: %s" \
- % (self._test_platform_name.upper(), build_type)
-
- for error in self._non_fatal_errors:
- logging.error(error)
- if len(self._errors):
- raise SyntaxError('\n'.join(map(str, self._errors)))
-
- # Now add in the tests that weren't present in the expectations file
- expectations = set([PASS])
- options = []
- modifiers = []
- if self._full_test_list:
- for test in self._full_test_list:
- if not test in self._test_list_paths:
- self._add_test(test, modifiers, expectations, options)
+ modifiers, options, overrides_allowed)
def _get_options_list(self, listString):
return [part.strip().lower() for part in listString.strip().split(' ')]
@@ -692,15 +761,18 @@ class TestExpectationsFile:
return path
def _add_tests(self, tests, expectations, test_list_path, lineno,
- modifiers, options):
+ modifiers, options, overrides_allowed):
for test in tests:
- if self._already_seen_test(test, test_list_path, lineno):
+ if self._already_seen_test(test, test_list_path, lineno,
+ overrides_allowed):
continue
self._clear_expectations_for_test(test, test_list_path)
- self._add_test(test, modifiers, expectations, options)
+ self._add_test(test, modifiers, expectations, options,
+ overrides_allowed)
- def _add_test(self, test, modifiers, expectations, options):
+ def _add_test(self, test, modifiers, expectations, options,
+ overrides_allowed):
"""Sets the expected state for a given test.
This routine assumes the test has not been added before. If it has,
@@ -711,7 +783,9 @@ class TestExpectationsFile:
test: test to add
modifiers: sequence of modifier keywords ('wontfix', 'slow', etc.)
expectations: sequence of expectations (PASS, IMAGE, etc.)
- options: sequence of keywords and bug identifiers."""
+ options: sequence of keywords and bug identifiers.
+ overrides_allowed: whether we're parsing the regular expectations
+ or the overridding expectations"""
self._test_to_expectations[test] = expectations
for expectation in expectations:
self._expectation_to_tests[expectation].add(test)
@@ -739,6 +813,9 @@ class TestExpectationsFile:
else:
self._result_type_to_tests[FAIL].add(test)
+ if overrides_allowed:
+ self._overridding_tests.add(test)
+
def _clear_expectations_for_test(self, test, test_list_path):
"""Remove prexisting expectations for this test.
This happens if we are seeing a more precise path
@@ -763,7 +840,8 @@ class TestExpectationsFile:
if test in set_of_tests:
set_of_tests.remove(test)
- def _already_seen_test(self, test, test_list_path, lineno):
+ def _already_seen_test(self, test, test_list_path, lineno,
+ allow_overrides):
"""Returns true if we've already seen a more precise path for this test
than the test_list_path.
"""
@@ -772,8 +850,19 @@ class TestExpectationsFile:
prev_base_path = self._test_list_paths[test]
if (prev_base_path == os.path.normpath(test_list_path)):
- self._add_error(lineno, 'Duplicate expectations.', test)
- return True
+ if (not allow_overrides or test in self._overridding_tests):
+ if allow_overrides:
+ expectation_source = "override"
+ else:
+ expectation_source = "expectation"
+ self._add_error(lineno, 'Duplicate %s.' % expectation_source,
+ test)
+ return True
+ else:
+ # We have seen this path, but that's okay because its
+ # in the overrides and the earlier path was in the
+ # expectations.
+ return False
# Check if we've already seen a more precise path.
return prev_base_path.startswith(os.path.normpath(test_list_path))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py
new file mode 100644
index 0000000..d11f3e2
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# Copyright (C) 2010 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.
+
+"""Unit tests for test_expectations.py."""
+
+import os
+import sys
+import unittest
+
+try:
+ d = os.path.dirname(__file__)
+except NameError:
+ d = os.path.dirname(sys.argv[0])
+
+sys.path.append(os.path.abspath(os.path.join(d, '..')))
+sys.path.append(os.path.abspath(os.path.join(d, '../../thirdparty')))
+
+import port
+from test_expectations import *
+
+class FunctionsTest(unittest.TestCase):
+ def test_result_was_expected(self):
+ # test basics
+ self.assertEquals(result_was_expected(PASS, set([PASS]),
+ False, False), True)
+ self.assertEquals(result_was_expected(TEXT, set([PASS]),
+ False, False), False)
+
+ # test handling of FAIL expectations
+ self.assertEquals(result_was_expected(IMAGE_PLUS_TEXT, set([FAIL]),
+ False, False), True)
+ self.assertEquals(result_was_expected(IMAGE, set([FAIL]),
+ False, False), True)
+ self.assertEquals(result_was_expected(TEXT, set([FAIL]),
+ False, False), True)
+ self.assertEquals(result_was_expected(CRASH, set([FAIL]),
+ False, False), False)
+
+ # test handling of SKIPped tests and results
+ self.assertEquals(result_was_expected(SKIP, set([CRASH]),
+ False, True), True)
+ self.assertEquals(result_was_expected(SKIP, set([CRASH]),
+ False, False), False)
+
+ # test handling of MISSING results and the REBASELINE modifier
+ self.assertEquals(result_was_expected(MISSING, set([PASS]),
+ True, False), True)
+ self.assertEquals(result_was_expected(MISSING, set([PASS]),
+ False, False), False)
+
+ def test_remove_pixel_failures(self):
+ self.assertEquals(remove_pixel_failures(set([TEXT])),
+ set([TEXT]))
+ self.assertEquals(remove_pixel_failures(set([PASS])),
+ set([PASS]))
+ self.assertEquals(remove_pixel_failures(set([IMAGE])),
+ set([PASS]))
+ self.assertEquals(remove_pixel_failures(set([IMAGE_PLUS_TEXT])),
+ set([TEXT]))
+ self.assertEquals(remove_pixel_failures(set([PASS, IMAGE, CRASH])),
+ set([PASS, CRASH]))
+
+
+class TestExpectationsTest(unittest.TestCase):
+
+ def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
+ self._port = port.get('test', None)
+ self._exp = None
+ unittest.TestCase.__init__(self, testFunc)
+
+ def get_test(self, test_name):
+ return os.path.join(self._port.layout_tests_dir(), test_name)
+
+ def get_basic_tests(self):
+ return [self.get_test('fast/html/article-element.html'),
+ self.get_test('fast/html/header-element.html'),
+ self.get_test('fast/html/keygen.html'),
+ self.get_test('fast/html/tab-order.html'),
+ self.get_test('fast/events/space-scroll-event.html'),
+ self.get_test('fast/events/tab-imagemap.html')]
+
+ def get_basic_expectations(self):
+ return """
+BUG_TEST : fast/html/article-element.html = TEXT
+BUG_TEST SKIP : fast/html/keygen.html = CRASH
+BUG_TEST REBASELINE : fast/htmltab-order.html = MISSING
+BUG_TEST : fast/events = IMAGE
+"""
+
+ def parse_exp(self, expectations, overrides=None):
+ self._exp = TestExpectations(self._port,
+ tests=self.get_basic_tests(),
+ expectations=expectations,
+ test_platform_name=self._port.test_platform_name(),
+ is_debug_mode=False,
+ is_lint_mode=False,
+ tests_are_present=True,
+ overrides=overrides)
+
+ def assert_exp(self, test, result):
+ self.assertEquals(self._exp.get_expectations(self.get_test(test)),
+ set([result]))
+
+ def test_basic(self):
+ self.parse_exp(self.get_basic_expectations())
+ self.assert_exp('fast/html/article-element.html', TEXT)
+ self.assert_exp('fast/events/tab-imagemap.html', IMAGE)
+ self.assert_exp('fast/html/header-element.html', PASS)
+
+ def test_duplicates(self):
+ self.assertRaises(SyntaxError, self.parse_exp, """
+BUG_TEST : fast/html/article-element.html = TEXT
+BUG_TEST : fast/html/article-element.html = IMAGE""")
+ self.assertRaises(SyntaxError, self.parse_exp,
+ self.get_basic_expectations(), """
+BUG_TEST : fast/html/article-element.html = TEXT
+BUG_TEST : fast/html/article-element.html = IMAGE""")
+
+ def test_overrides(self):
+ self.parse_exp(self.get_basic_expectations(), """
+BUG_OVERRIDE : fast/html/article-element.html = IMAGE""")
+ self.assert_exp('fast/html/article-element.html', IMAGE)
+
+ def test_matches_an_expected_result(self):
+
+ def match(test, result, pixel_tests_enabled):
+ return self._exp.matches_an_expected_result(
+ self.get_test(test), result, pixel_tests_enabled)
+
+ self.parse_exp(self.get_basic_expectations())
+ self.assertTrue(match('fast/html/article-element.html', TEXT, True))
+ self.assertTrue(match('fast/html/article-element.html', TEXT, False))
+ self.assertFalse(match('fast/html/article-element.html', CRASH, True))
+ self.assertFalse(match('fast/html/article-element.html', CRASH, False))
+
+ self.assertTrue(match('fast/events/tab-imagemap.html', IMAGE, True))
+ self.assertTrue(match('fast/events/tab-imagemap.html', PASS, False))
+
+ self.assertTrue(match('fast/html/keygen.html', SKIP, False))
+ self.assertTrue(match('fast/html/tab-order.html', PASS, False))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
index 56d7b5a..60bdbca 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
@@ -79,8 +79,8 @@ class TestFailure(object):
"""Returns an HTML string to be included on the results.html page."""
raise NotImplemented
- def should_kill_test_shell(self):
- """Returns True if we should kill the test shell before the next
+ def should_kill_dump_render_tree(self):
+ """Returns True if we should kill DumpRenderTree before the next
test."""
return False
@@ -110,7 +110,7 @@ class FailureWithType(TestFailure):
def __init__(self, test_type):
TestFailure.__init__(self)
- # TODO(ojan): This class no longer needs to know the test_type.
+ # FIXME: This class no longer needs to know the test_type.
self._test_type = test_type
# Filename suffixes used by ResultHtmlOutput.
@@ -127,6 +127,9 @@ class FailureWithType(TestFailure):
single item is the [actual] filename suffix.
If out_names is empty, returns the empty string.
"""
+ # FIXME: Seems like a bad idea to separate the display name data
+ # from the path data by hard-coding the display name here
+ # and passing in the path information via out_names.
links = ['']
uris = [self.relative_output_filename(filename, fn) for
fn in out_names]
@@ -138,6 +141,8 @@ class FailureWithType(TestFailure):
links.append("<a href='%s'>diff</a>" % uris[2])
if len(uris) > 3:
links.append("<a href='%s'>wdiff</a>" % uris[3])
+ if len(uris) > 4:
+ links.append("<a href='%s'>pretty diff</a>" % uris[4])
return ' '.join(links)
def result_html_output(self, filename):
@@ -145,7 +150,7 @@ class FailureWithType(TestFailure):
class FailureTimeout(TestFailure):
- """Test timed out. We also want to restart the test shell if this
+ """Test timed out. We also want to restart DumpRenderTree if this
happens."""
@staticmethod
@@ -155,7 +160,7 @@ class FailureTimeout(TestFailure):
def result_html_output(self, filename):
return "<strong>%s</strong>" % self.message()
- def should_kill_test_shell(self):
+ def should_kill_dump_render_tree(self):
return True
@@ -172,7 +177,7 @@ class FailureCrash(TestFailure):
return "<strong>%s</strong> <a href=%s>stack</a>" % (self.message(),
stack)
- def should_kill_test_shell(self):
+ def should_kill_dump_render_tree(self):
return True
@@ -192,9 +197,10 @@ class FailureMissingResult(FailureWithType):
class FailureTextMismatch(FailureWithType):
"""Text diff output failed."""
# Filename suffixes used by ResultHtmlOutput.
+ # FIXME: Why don't we use the constants from TestTypeBase here?
OUT_FILENAMES = ["-actual.txt", "-expected.txt", "-diff.txt"]
OUT_FILENAMES_WDIFF = ["-actual.txt", "-expected.txt", "-diff.txt",
- "-wdiff.html"]
+ "-wdiff.html", "-pretty-diff.html"]
def __init__(self, test_type, has_wdiff):
FailureWithType.__init__(self, test_type)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_files.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_files.py
index 3c087c0..6754fa6 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_files.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_files.py
@@ -36,9 +36,16 @@ under that directory."""
import glob
import os
+import time
+
+from webkitpy.common.system import logutils
+
+
+_log = logutils.get_logger(__file__)
+
# When collecting test cases, we include any file with these extensions.
-_supported_file_extensions = set(['.html', '.shtml', '.xml', '.xhtml', '.pl',
+_supported_file_extensions = set(['.html', '.shtml', '.xml', '.xhtml', '.xhtmlmp', '.pl',
'.php', '.svg'])
# When collecting test cases, skip these directories
_skipped_directories = set(['.svn', '_svn', 'resources', 'script-tests'])
@@ -51,6 +58,7 @@ def gather_test_files(port, paths):
paths: a list of command line paths relative to the webkit/tests
directory. glob patterns are ok.
"""
+ gather_start_time = time.time()
paths_to_walk = set()
# if paths is empty, provide a pre-defined list.
if paths:
@@ -73,10 +81,16 @@ def gather_test_files(port, paths):
continue
for root, dirs, files in os.walk(path):
- # don't walk skipped directories and sub directories
+ # Don't walk skipped directories or their sub-directories.
if os.path.basename(root) in _skipped_directories:
del dirs[:]
continue
+ # This copy and for-in is slightly inefficient, but
+ # the extra walk avoidance consistently shaves .5 seconds
+ # off of total walk() time on my MacBook Pro.
+ for directory in dirs[:]:
+ if directory in _skipped_directories:
+ dirs.remove(directory)
for filename in files:
if _has_supported_extension(filename):
@@ -84,6 +98,9 @@ def gather_test_files(port, paths):
filename = os.path.normpath(filename)
test_files.add(filename)
+ gather_time = time.time() - gather_start_time
+ _log.debug("Test gathering took %f seconds" % gather_time)
+
return test_files
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py
index 3509675..e3ad6f4 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py
@@ -29,37 +29,4 @@
"""Port-specific entrypoints for the layout tests test infrastructure."""
-
-import sys
-
-
-def get(port_name=None, options=None):
- """Returns an object implementing the Port interface. If
- port_name is None, this routine attempts to guess at the most
- appropriate port on this platform."""
- port_to_use = port_name
- if port_to_use is None:
- if sys.platform == 'win32':
- port_to_use = 'chromium-win'
- elif sys.platform == 'linux2':
- port_to_use = 'chromium-linux'
- elif sys.platform == 'darwin':
- port_to_use = 'chromium-mac'
-
- if port_to_use == 'test':
- import test
- return test.TestPort(port_name, options)
- elif port_to_use.startswith('mac'):
- import mac
- return mac.MacPort(port_name, options)
- elif port_to_use.startswith('chromium-mac'):
- import chromium_mac
- return chromium_mac.ChromiumMacPort(port_name, options)
- elif port_to_use.startswith('chromium-linux'):
- import chromium_linux
- return chromium_linux.ChromiumLinuxPort(port_name, options)
- elif port_to_use.startswith('chromium-win'):
- import chromium_win
- return chromium_win.ChromiumWinPort(port_name, options)
-
- raise NotImplementedError('unsupported port: %s' % port_to_use)
+from factory import get
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py
index 9ff3671..1dd5b93 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py
@@ -38,6 +38,8 @@ import sys
import http_server_base
+_log = logging.getLogger("webkitpy.layout_tests.port.apache_http_server")
+
class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
@@ -77,14 +79,15 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
error_log = self._cygwin_safe_join(output_dir, "error_log.txt")
document_root = self._cygwin_safe_join(test_dir, "http", "tests")
+ # FIXME: We shouldn't be calling a protected method of _port_obj!
executable = self._port_obj._path_to_apache()
if self._is_cygwin():
executable = self._get_cygwin_path(executable)
cmd = [executable,
- '-f', self._get_apache_config_file_path(test_dir, output_dir),
- '-C', "\'DocumentRoot %s\'" % document_root,
- '-c', "\'Alias /js-test-resources %s\'" % js_test_resources_dir,
+ '-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_dir),
+ '-C', "\'DocumentRoot \"%s\"\'" % document_root,
+ '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir,
'-C', "\'Listen %s\'" % "127.0.0.1:8000",
'-C', "\'Listen %s\'" % "127.0.0.1:8081",
'-c', "\'TypesConfig \"%s\"\'" % mime_types_path,
@@ -174,7 +177,7 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
It will listen to 127.0.0.1 on each of the given port.
"""
return '\n'.join(('<VirtualHost 127.0.0.1:%s>' % port,
- 'DocumentRoot %s' % document_root,
+ 'DocumentRoot "%s"' % document_root,
ssl and 'SSLEngine On' or '',
'</VirtualHost>', ''))
@@ -188,7 +191,7 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
shell=True)
err = self._httpd_proc.stderr.read()
if len(err):
- logging.debug(err)
+ _log.debug(err)
return False
return True
@@ -197,22 +200,23 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
# Stop any currently running servers.
self.stop()
- logging.debug("Starting apache http server")
+ _log.debug("Starting apache http server")
server_started = self.wait_for_action(self._start_httpd_process)
if server_started:
- logging.debug("Apache started. Testing ports")
+ _log.debug("Apache started. Testing ports")
server_started = self.wait_for_action(
self.is_server_running_on_all_ports)
if server_started:
- logging.debug("Server successfully started")
+ _log.debug("Server successfully started")
else:
raise Exception('Failed to start http server')
def stop(self):
"""Stops the apache http server."""
- logging.debug("Shutting down any running http servers")
+ _log.debug("Shutting down any running http servers")
httpd_pid = None
if os.path.exists(self._pid_file):
httpd_pid = int(open(self._pid_file).readline())
+ # FIXME: We shouldn't be calling a protected method of _port_obj!
self._port_obj._shut_down_http_server(httpd_pid)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
index 2b25e29..fb6fddf 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
@@ -36,27 +36,49 @@ import errno
import os
import subprocess
import sys
+import time
import apache_http_server
import http_server
import websocket_server
+from webkitpy.common.system import logutils
+from webkitpy.common.system.executive import Executive, ScriptError
+
+
+_log = logutils.get_logger(__file__)
+
+
# Python bug workaround. See Port.wdiff_text() for an explanation.
_wdiff_available = True
-
+_pretty_patch_available = True
# FIXME: This class should merge with webkitpy.webkit_port at some point.
class Port(object):
"""Abstract class for Port-specific hooks for the layout_test package.
"""
- def __init__(self, port_name=None, options=None):
+ @staticmethod
+ def flag_from_configuration(configuration):
+ flags_by_configuration = {
+ "Debug": "--debug",
+ "Release": "--release",
+ }
+ return flags_by_configuration[configuration]
+
+ def __init__(self, port_name=None, options=None, executive=Executive()):
self._name = port_name
self._options = options
self._helper = None
self._http_server = None
self._webkit_base_dir = None
self._websocket_server = None
+ self._executive = executive
+
+ def default_child_processes(self):
+ """Return the number of DumpRenderTree instances to use for this
+ port."""
+ return self._executive.cpu_count()
def baseline_path(self):
"""Return the absolute path to the directory to store new baselines
@@ -68,38 +90,53 @@ class Port(object):
baselines. The directories are searched in order."""
raise NotImplementedError('Port.baseline_search_path')
- def check_sys_deps(self):
+ def check_build(self, needs_http):
+ """This routine is used to ensure that the build is up to date
+ and all the needed binaries are present."""
+ raise NotImplementedError('Port.check_build')
+
+ def check_sys_deps(self, needs_http):
"""If the port needs to do some runtime checks to ensure that the
- tests can be run successfully, they should be done here.
+ tests can be run successfully, it should override this routine.
+ This step can be skipped with --nocheck-sys-deps.
Returns whether the system is properly configured."""
- raise NotImplementedError('Port.check_sys_deps')
+ return True
+
+ def check_image_diff(self, override_step=None, logging=True):
+ """This routine is used to check whether image_diff binary exists."""
+ raise NotImplemented('Port.check_image_diff')
- def compare_text(self, actual_text, expected_text):
+ def compare_text(self, expected_text, actual_text):
"""Return whether or not the two strings are *not* equal. This
routine is used to diff text output.
While this is a generic routine, we include it in the Port
interface so that it can be overriden for testing purposes."""
- return actual_text != expected_text
+ return expected_text != actual_text
- def diff_image(self, actual_filename, expected_filename,
+ def diff_image(self, expected_filename, actual_filename,
diff_filename=None):
"""Compare two image files and produce a delta image file.
- Return 1 if the two files are different, 0 if they are the same.
+ Return True if the two files are different, False if they are the same.
Also produce a delta image of the two images and write that into
|diff_filename| if it is not None.
While this is a generic routine, we include it in the Port
interface so that it can be overriden for testing purposes."""
executable = self._path_to_image_diff()
- cmd = [executable, '--diff', actual_filename, expected_filename]
+
if diff_filename:
- cmd.append(diff_filename)
- result = 1
+ cmd = [executable, '--diff', expected_filename, actual_filename,
+ diff_filename]
+ else:
+ cmd = [executable, expected_filename, actual_filename]
+
+ result = True
try:
- result = subprocess.call(cmd)
+ if subprocess.call(cmd) == 0:
+ return False
except OSError, e:
if e.errno == errno.ENOENT or e.errno == errno.EACCES:
_compare_available = False
@@ -111,8 +148,8 @@ class Port(object):
pass
return result
- def diff_text(self, actual_text, expected_text,
- actual_filename, expected_filename):
+ def diff_text(self, expected_text, actual_text,
+ expected_filename, actual_filename):
"""Returns a string containing the diff of the two text strings
in 'unified diff' format.
@@ -124,6 +161,13 @@ class Port(object):
actual_filename)
return ''.join(diff)
+ def driver_name(self):
+ """Returns the name of the actual binary that is performing the test,
+ so that it can be referred to in log messages. In most cases this
+ will be DumpRenderTree, but if a port uses a binary with a different
+ name, it can be overridden here."""
+ return "DumpRenderTree"
+
def expected_baselines(self, filename, suffix, all_baselines=False):
"""Given a test name, finds where the baseline results are located.
@@ -262,14 +306,7 @@ class Port(object):
may be different (e.g., 'win-xp' instead of 'chromium-win-xp'."""
return self._name
- def num_cores(self):
- """Return the number of cores/cpus available on this machine.
-
- This routine is used to determine the default amount of parallelism
- used by run-chromium-webkit-tests."""
- raise NotImplementedError('Port.num_cores')
-
- # FIXME: This could be replaced by functions in webkitpy.scm.
+ # FIXME: This could be replaced by functions in webkitpy.common.checkout.scm.
def path_from_webkit_base(self, *comps):
"""Returns the full path to path made by joining the top of the
WebKit source tree and the list of path components in |*comps|."""
@@ -288,7 +325,7 @@ class Port(object):
This is used by the rebaselining tool. Raises NotImplementedError
if the port does not use expectations files."""
raise NotImplementedError('Port.path_to_test_expectations_file')
-
+
def remove_directory(self, *path):
"""Recursively removes a directory, even if it's marked read-only.
@@ -321,7 +358,7 @@ class Port(object):
win32 = False
def remove_with_retry(rmfunc, path):
- os.chmod(path, stat.S_IWRITE)
+ os.chmod(path, os.stat.S_IWRITE)
if win32:
win32api.SetFileAttributes(path,
win32con.FILE_ATTRIBUTE_NORMAL)
@@ -381,10 +418,10 @@ class Port(object):
raise NotImplementedError('Port.start_driver')
def start_helper(self):
- """Start a layout test helper if needed on this port. The test helper
- is used to reconfigure graphics settings and do other things that
- may be necessary to ensure a known test configuration."""
- raise NotImplementedError('Port.start_helper')
+ """If a port needs to reconfigure graphics settings or do other
+ things to ensure a known test configuration, it should override this
+ method."""
+ pass
def start_http_server(self):
"""Start a web server if it is available. Do nothing if
@@ -408,8 +445,9 @@ class Port(object):
def stop_helper(self):
"""Shut down the test helper if it is running. Do nothing if
- it isn't, or it isn't available."""
- raise NotImplementedError('Port.stop_helper')
+ it isn't, or it isn't available. If a port overrides start_helper()
+ it must override this routine as well."""
+ pass
def stop_http_server(self):
"""Shut down the http server if it is running. Do nothing if
@@ -430,6 +468,15 @@ class Port(object):
test_expectations file. See test_expectations.py for more details."""
raise NotImplementedError('Port.test_expectations')
+ def test_expectations_overrides(self):
+ """Returns an optional set of overrides for the test_expectations.
+
+ This is used by ports that have code in two repositories, and where
+ it is possible that you might need "downstream" expectations that
+ temporarily override the "upstream" expectations until the port can
+ sync up the two repos."""
+ return None
+
def test_base_platform_names(self):
"""Return a list of the 'base' platforms on your port. The base
platforms represent different architectures, operating systems,
@@ -458,6 +505,12 @@ class Port(object):
might return 'mac' as a test_platform name'."""
raise NotImplementedError('Port.platforms')
+ def test_platform_name_to_name(self, test_platform_name):
+ """Returns the Port platform name that corresponds to the name as
+ referenced in the expectations file. E.g., "mac" returns
+ "chromium-mac" on the Chromium ports."""
+ raise NotImplementedError('Port.test_platform_name_to_name')
+
def version(self):
"""Returns a string indicating the version of a given platform, e.g.
'-leopard' or '-xp'.
@@ -476,8 +529,9 @@ class Port(object):
'--end-delete=##WDIFF_END##',
'--start-insert=##WDIFF_ADD##',
'--end-insert=##WDIFF_END##',
- expected_filename,
- actual_filename]
+ actual_filename,
+ expected_filename]
+ # FIXME: Why not just check os.exists(executable) once?
global _wdiff_available
result = ''
try:
@@ -500,6 +554,7 @@ class Port(object):
# http://bugs.python.org/issue1236
if _wdiff_available:
try:
+ # FIXME: Use Executive() here.
wdiff = subprocess.Popen(cmd,
stdout=subprocess.PIPE).communicate()[0]
except ValueError, e:
@@ -521,6 +576,31 @@ class Port(object):
raise e
return result
+ _pretty_patch_error_html = "Failed to run PrettyPatch, see error console."
+
+ def pretty_patch_text(self, diff_path):
+ global _pretty_patch_available
+ if not _pretty_patch_available:
+ return self._pretty_patch_error_html
+ pretty_patch_path = self.path_from_webkit_base("BugsSite", "PrettyPatch")
+ prettify_path = os.path.join(pretty_patch_path, "prettify.rb")
+ command = ["ruby", "-I", pretty_patch_path, prettify_path, diff_path]
+ try:
+ return self._executive.run_command(command)
+ except OSError, e:
+ # If the system is missing ruby log the error and stop trying.
+ _pretty_patch_available = False
+ _log.error("Failed to run PrettyPatch (%s): %s" % (command, e))
+ return self._pretty_patch_error_html
+ except ScriptError, e:
+ # If ruby failed to run for some reason, log the command output and stop trying.
+ _pretty_patch_available = False
+ _log.error("Failed to run PrettyPatch (%s):\n%s" % (command, e.message_with_output()))
+ return self._pretty_patch_error_html
+
+ def default_configuration(self):
+ return "Release"
+
#
# PROTECTED ROUTINES
#
@@ -528,13 +608,6 @@ class Port(object):
# or any of its subclasses.
#
- def _kill_process(self, pid):
- """Forcefully kill a process.
-
- This routine should not be used or needed generically, but can be
- used in helper files like http_server.py."""
- raise NotImplementedError('Port.kill_process')
-
def _path_to_apache(self):
"""Returns the full path to the apache binary.
@@ -547,7 +620,7 @@ class Port(object):
This is needed only by ports that use the apache_http_server module."""
raise NotImplementedError('Port.path_to_apache_config_file')
- def _path_to_driver(self):
+ def _path_to_driver(self, configuration=None):
"""Returns the full path to the test driver (DumpRenderTree)."""
raise NotImplementedError('Port.path_to_driver')
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
index 1123376..8bae2a9 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
@@ -36,11 +36,43 @@ import signal
import subprocess
import sys
import time
+import webbrowser
import base
import http_server
+
+# FIXME: To use the DRT-based version of this file, we need to be able to
+# run the webkit code, which uses server_process, which requires UNIX-style
+# non-blocking I/O with selects(), which requires fcntl() which doesn't exist
+# on Windows.
+if sys.platform not in ('win32', 'cygwin'):
+ import webkit
+
import websocket_server
+_log = logging.getLogger("webkitpy.layout_tests.port.chromium")
+
+
+# FIXME: This function doesn't belong in this package.
+def check_file_exists(path_to_file, file_description, override_step=None,
+ logging=True):
+ """Verify the file is present where expected or log an error.
+
+ Args:
+ file_name: The (human friendly) name or description of the file
+ you're looking for (e.g., "HTTP Server"). Used for error logging.
+ override_step: An optional string to be logged if the check fails.
+ logging: Whether or not log the error messages."""
+ if not os.path.exists(path_to_file):
+ if logging:
+ _log.error('Unable to find %s' % file_description)
+ _log.error(' at %s' % path_to_file)
+ if override_step:
+ _log.error(' %s' % override_step)
+ _log.error('')
+ return False
+ return True
+
class ChromiumPort(base.Port):
"""Abstract base class for Chromium implementations of the Port class."""
@@ -50,81 +82,116 @@ class ChromiumPort(base.Port):
self._chromium_base_dir = None
def baseline_path(self):
- return self._chromium_baseline_path(self._name)
+ return self._webkit_baseline_path(self._name)
- def check_sys_deps(self):
+ def check_build(self, needs_http):
result = True
- test_shell_binary_path = self._path_to_driver()
- if os.path.exists(test_shell_binary_path):
- proc = subprocess.Popen([test_shell_binary_path,
- '--check-layout-test-sys-deps'])
- if proc.wait() != 0:
- logging.error("Aborting because system dependencies check "
- "failed.")
- logging.error("To override, invoke with --nocheck-sys-deps")
- result = False
- else:
- logging.error('test driver is not found at %s' %
- test_shell_binary_path)
- result = False
- image_diff_path = self._path_to_image_diff()
- if (not os.path.exists(image_diff_path) and not
- self._options.no_pixel_tests):
- logging.error('image diff not found at %s' % image_diff_path)
- logging.error("To override, invoke with --no-pixel-tests")
+ # FIXME: see comment above re: import webkit
+ if (sys.platform in ('win32', 'cygwin') and self._options and
+ hasattr(self._options, 'use_drt') and self._options.use_drt):
+ _log.error('--use-drt is not supported on Windows yet')
+ _log.error('')
result = False
+ dump_render_tree_binary_path = self._path_to_driver()
+ result = check_file_exists(dump_render_tree_binary_path,
+ 'test driver') and result
+ if result and self._options.build:
+ result = self._check_driver_build_up_to_date(
+ self._options.configuration)
+ else:
+ _log.error('')
+
+ helper_path = self._path_to_helper()
+ if helper_path:
+ result = check_file_exists(helper_path,
+ 'layout test helper') and result
+
+ if self._options.pixel_tests:
+ result = self.check_image_diff(
+ 'To override, invoke with --no-pixel-tests') and result
+
return result
- def compare_text(self, actual_text, expected_text):
- return actual_text != expected_text
+ def check_sys_deps(self, needs_http):
+ dump_render_tree_binary_path = self._path_to_driver()
+ proc = subprocess.Popen([dump_render_tree_binary_path,
+ '--check-layout-test-sys-deps'])
+ if proc.wait():
+ _log.error('System dependencies check failed.')
+ _log.error('To override, invoke with --nocheck-sys-deps')
+ _log.error('')
+ return False
+ return True
+
+ def check_image_diff(self, override_step=None, logging=True):
+ image_diff_path = self._path_to_image_diff()
+ return check_file_exists(image_diff_path, 'image diff exe',
+ override_step, logging)
+
+ def driver_name(self):
+ return "test_shell"
def path_from_chromium_base(self, *comps):
"""Returns the full path to path made by joining the top of the
Chromium source tree and the list of path components in |*comps|."""
if not self._chromium_base_dir:
abspath = os.path.abspath(__file__)
- self._chromium_base_dir = abspath[0:abspath.find('third_party')]
+ offset = abspath.find('third_party')
+ if offset == -1:
+ raise AssertionError('could not find Chromium base dir from ' +
+ abspath)
+ self._chromium_base_dir = abspath[0:offset]
return os.path.join(self._chromium_base_dir, *comps)
def path_to_test_expectations_file(self):
- return self.path_from_chromium_base('webkit', 'tools', 'layout_tests',
- 'test_expectations.txt')
+ return self.path_from_webkit_base('LayoutTests', 'platform',
+ 'chromium', 'test_expectations.txt')
def results_directory(self):
- return self.path_from_chromium_base('webkit', self._options.target,
- self._options.results_directory)
+ try:
+ return self.path_from_chromium_base('webkit',
+ self._options.configuration, self._options.results_directory)
+ except AssertionError:
+ return self.path_from_webkit_base('WebKit', 'chromium',
+ 'xcodebuild', self._options.configuration,
+ self._options.results_directory)
def setup_test_run(self):
# Delete the disk cache if any to ensure a clean test run.
- test_shell_binary_path = self._path_to_driver()
- cachedir = os.path.split(test_shell_binary_path)[0]
+ dump_render_tree_binary_path = self._path_to_driver()
+ cachedir = os.path.split(dump_render_tree_binary_path)[0]
cachedir = os.path.join(cachedir, "cache")
if os.path.exists(cachedir):
shutil.rmtree(cachedir)
def show_results_html_file(self, results_filename):
- subprocess.Popen([self._path_to_driver(),
- self.filename_to_uri(results_filename)])
+ uri = self.filename_to_uri(results_filename)
+ if self._options.use_drt:
+ webbrowser.open(uri, new=1)
+ else:
+ subprocess.Popen([self._path_to_driver(), uri])
def start_driver(self, image_path, options):
"""Starts a new Driver and returns a handle to it."""
+ if self._options.use_drt:
+ return webkit.WebKitDriver(self, image_path, options)
return ChromiumDriver(self, image_path, options)
def start_helper(self):
helper_path = self._path_to_helper()
if helper_path:
- logging.debug("Starting layout helper %s" % helper_path)
+ _log.debug("Starting layout helper %s" % helper_path)
self._helper = subprocess.Popen([helper_path],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None)
is_ready = self._helper.stdout.readline()
if not is_ready.startswith('ready'):
- logging.error("layout_test_helper failed to be ready")
+ _log.error("layout_test_helper failed to be ready")
def stop_helper(self):
if self._helper:
- logging.debug("Stopping layout test helper")
+ _log.debug("Stopping layout test helper")
self._helper.stdin.write("x\n")
self._helper.stdin.close()
self._helper.wait()
@@ -140,10 +207,27 @@ class ChromiumPort(base.Port):
expectations_file = self.path_to_test_expectations_file()
return file(expectations_file, "r").read()
+ def test_expectations_overrides(self):
+ try:
+ overrides_file = self.path_from_chromium_base('webkit', 'tools',
+ 'layout_tests', 'test_expectations.txt')
+ except AssertionError:
+ return None
+ if os.path.exists(overrides_file):
+ return file(overrides_file, "r").read()
+ else:
+ return None
+
def test_platform_names(self):
return self.test_base_platform_names() + ('win-xp',
'win-vista', 'win-7')
+ def test_platform_name_to_name(self, test_platform_name):
+ if test_platform_name in self.test_platform_names():
+ return 'chromium-' + test_platform_name
+ raise ValueError('Unsupported test_platform_name: %s' %
+ test_platform_name)
+
#
# PROTECTED METHODS
#
@@ -151,11 +235,34 @@ class ChromiumPort(base.Port):
# or any subclasses.
#
+ def _check_driver_build_up_to_date(self, configuration):
+ if configuration in ('Debug', 'Release'):
+ try:
+ debug_path = self._path_to_driver('Debug')
+ release_path = self._path_to_driver('Release')
+
+ debug_mtime = os.stat(debug_path).st_mtime
+ release_mtime = os.stat(release_path).st_mtime
+
+ if (debug_mtime > release_mtime and configuration == 'Release' or
+ release_mtime > debug_mtime and configuration == 'Debug'):
+ _log.warning('You are not running the most '
+ 'recent DumpRenderTree binary. You need to '
+ 'pass --debug or not to select between '
+ 'Debug and Release.')
+ _log.warning('')
+ # This will fail if we don't have both a debug and release binary.
+ # That's fine because, in this case, we must already be running the
+ # most up-to-date one.
+ except OSError:
+ pass
+ return True
+
def _chromium_baseline_path(self, platform):
if platform is None:
platform = self.name()
- return self.path_from_chromium_base('webkit', 'data', 'layout_tests',
- 'platform', platform, 'LayoutTests')
+ return self.path_from_webkit_base('LayoutTests', 'platform', platform)
+
class ChromiumDriver(base.Driver):
"""Abstract interface for the DumpRenderTree interface."""
@@ -163,7 +270,7 @@ class ChromiumDriver(base.Driver):
def __init__(self, port, image_path, options):
self._port = port
self._options = options
- self._target = port._options.target
+ self._configuration = port._options.configuration
self._image_path = image_path
cmd = []
@@ -181,10 +288,17 @@ class ChromiumDriver(base.Driver):
cmd += [port._path_to_driver(), '--layout-tests']
if options:
cmd += options
+
+ # We need to pass close_fds=True to work around Python bug #2320
+ # (otherwise we can hang when we kill DumpRenderTree when we are running
+ # multiple threads). See http://bugs.python.org/issue2320 .
+ # Note that close_fds isn't supported on Windows, but this bug only
+ # shows up on Mac and Linux.
+ close_flag = sys.platform not in ('win32', 'cygwin')
self._proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
-
+ stderr=subprocess.STDOUT,
+ close_fds=close_flag)
def poll(self):
return self._proc.poll()
@@ -207,14 +321,19 @@ class ChromiumDriver(base.Driver):
cmd += ' ' + checksum
cmd += "\n"
- self._proc.stdin.write(cmd)
- line = self._proc.stdout.readline()
- while line.rstrip() != "#EOF":
+ try:
+ self._proc.stdin.write(cmd)
+ line = self._proc.stdout.readline()
+ except IOError, e:
+ _log.error("IOError communicating w/ test_shell: " + str(e))
+ crash = True
+
+ while not crash and line.rstrip() != "#EOF":
# Make sure we haven't crashed.
if line == '' and self.poll() is not None:
# This is hex code 0xc000001d, which is used for abrupt
# termination. This happens if we hit ctrl+c from the prompt
- # and we happen to be waiting on the test_shell.
+ # and we happen to be waiting on the DumpRenderTree.
# sdoyon: Not sure for which OS and in what circumstances the
# above code is valid. What works for me under Linux to detect
# ctrl+c is for the subprocess returncode to be negative
@@ -229,8 +348,8 @@ class ChromiumDriver(base.Driver):
if line.startswith("#URL:"):
actual_uri = line.rstrip()[5:]
if uri != actual_uri:
- logging.fatal("Test got out of sync:\n|%s|\n|%s|" %
- (uri, actual_uri))
+ _log.fatal("Test got out of sync:\n|%s|\n|%s|" %
+ (uri, actual_uri))
raise AssertionError("test out of sync")
elif line.startswith("#MD5:"):
actual_checksum = line.rstrip()[5:]
@@ -242,7 +361,11 @@ class ChromiumDriver(base.Driver):
else:
error.append(line)
- line = self._proc.stdout.readline()
+ try:
+ line = self._proc.stdout.readline()
+ except IOError, e:
+ _log.error("IOError while reading: " + str(e))
+ crash = True
return (crash, timeout, actual_checksum, ''.join(output),
''.join(error))
@@ -253,10 +376,20 @@ class ChromiumDriver(base.Driver):
self._proc.stdout.close()
if self._proc.stderr:
self._proc.stderr.close()
- if (sys.platform not in ('win32', 'cygwin') and
- not self._proc.poll()):
- # Closing stdin/stdout/stderr hangs sometimes on OS X.
- null = open(os.devnull, "w")
- subprocess.Popen(["kill", "-9",
- str(self._proc.pid)], stderr=null)
- null.close()
+ if sys.platform not in ('win32', 'cygwin'):
+ # Closing stdin/stdout/stderr hangs sometimes on OS X,
+ # (see __init__(), above), and anyway we don't want to hang
+ # the harness if DumpRenderTree is buggy, so we wait a couple
+ # seconds to give DumpRenderTree a chance to clean up, but then
+ # force-kill the process if necessary.
+ KILL_TIMEOUT = 3.0
+ timeout = time.time() + KILL_TIMEOUT
+ while self._proc.poll() is None and time.time() < timeout:
+ time.sleep(0.1)
+ if self._proc.poll() is None:
+ _log.warning('stopping test driver timed out, '
+ 'killing it')
+ null = open(os.devnull, "w")
+ subprocess.Popen(["kill", "-9",
+ str(self._proc.pid)], stderr=null)
+ null.close()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py
index b817251..9a595f2 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py
@@ -27,8 +27,9 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Chromium Mac implementation of the Port interface."""
+"""Chromium Linux implementation of the Port interface."""
+import logging
import os
import platform
import signal
@@ -36,6 +37,8 @@ import subprocess
import chromium
+_log = logging.getLogger("webkitpy.layout_tests.port.chromium_linux")
+
class ChromiumLinuxPort(chromium.ChromiumPort):
"""Chromium Linux implementation of the Port class."""
@@ -43,25 +46,32 @@ class ChromiumLinuxPort(chromium.ChromiumPort):
def __init__(self, port_name=None, options=None):
if port_name is None:
port_name = 'chromium-linux'
- if options and not hasattr(options, 'target'):
- options.target = 'Release'
+ if options and not hasattr(options, 'configuration'):
+ options.configuration = 'Release'
chromium.ChromiumPort.__init__(self, port_name, options)
def baseline_search_path(self):
- return [self.baseline_path(),
- self._chromium_baseline_path('chromium-win'),
+ return [self._webkit_baseline_path('chromium-linux'),
+ self._webkit_baseline_path('chromium-win'),
+ self._webkit_baseline_path('chromium'),
self._webkit_baseline_path('win'),
self._webkit_baseline_path('mac')]
- def check_sys_deps(self):
- # We have no platform-specific dependencies to check.
- return True
-
- def num_cores(self):
- num_cores = os.sysconf("SC_NPROCESSORS_ONLN")
- if isinstance(num_cores, int) and num_cores > 0:
- return num_cores
- return 1
+ def check_build(self, needs_http):
+ result = chromium.ChromiumPort.check_build(self, needs_http)
+ if needs_http:
+ if self._options.use_apache:
+ result = self._check_apache_install() and result
+ else:
+ result = self._check_lighttpd_install() and result
+ result = self._check_wdiff_install() and result
+
+ if not result:
+ _log.error('For complete Linux build requirements, please see:')
+ _log.error('')
+ _log.error(' http://code.google.com/p/chromium/wiki/'
+ 'LinuxBuildInstructions')
+ return result
def test_platform_name(self):
# We use 'linux' instead of 'chromium-linux' in test_expectations.txt.
@@ -78,19 +88,42 @@ class ChromiumLinuxPort(chromium.ChromiumPort):
def _build_path(self, *comps):
base = self.path_from_chromium_base()
if os.path.exists(os.path.join(base, 'sconsbuild')):
- return self.path_from_chromium_base('sconsbuild',
- self._options.target, *comps)
+ return self.path_from_chromium_base('sconsbuild', *comps)
else:
- return self.path_from_chromium_base('out',
- self._options.target, *comps)
-
- def _kill_process(self, pid):
- """Forcefully kill the process.
+ return self.path_from_chromium_base('out', *comps)
+
+ def _check_apache_install(self):
+ result = chromium.check_file_exists(self._path_to_apache(),
+ "apache2")
+ result = chromium.check_file_exists(self._path_to_apache_config_file(),
+ "apache2 config file") and result
+ if not result:
+ _log.error(' Please install using: "sudo apt-get install '
+ 'apache2 libapache2-mod-php5"')
+ _log.error('')
+ return result
+
+ def _check_lighttpd_install(self):
+ result = chromium.check_file_exists(
+ self._path_to_lighttpd(), "LigHTTPd executable")
+ result = chromium.check_file_exists(self._path_to_lighttpd_php(),
+ "PHP CGI executable") and result
+ result = chromium.check_file_exists(self._path_to_lighttpd_modules(),
+ "LigHTTPd modules") and result
+ if not result:
+ _log.error(' Please install using: "sudo apt-get install '
+ 'lighttpd php5-cgi"')
+ _log.error('')
+ return result
+
+ def _check_wdiff_install(self):
+ result = chromium.check_file_exists(self._path_to_wdiff(), 'wdiff')
+ if not result:
+ _log.error(' Please install using: "sudo apt-get install '
+ 'wdiff"')
+ _log.error('')
+ return result
- Args:
- pid: The id of the process to be killed.
- """
- os.kill(pid, signal.SIGKILL)
def _kill_all_process(self, process_name):
null = open(os.devnull)
@@ -99,11 +132,19 @@ class ChromiumLinuxPort(chromium.ChromiumPort):
null.close()
def _path_to_apache(self):
- return '/usr/sbin/apache2'
+ if self._is_redhat_based():
+ return '/usr/sbin/httpd'
+ else:
+ return '/usr/sbin/apache2'
def _path_to_apache_config_file(self):
+ if self._is_redhat_based():
+ config_name = 'fedora-httpd.conf'
+ else:
+ config_name = 'apache2-debian-httpd.conf'
+
return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'apache2-debian-httpd.conf')
+ config_name)
def _path_to_lighttpd(self):
return "/usr/sbin/lighttpd"
@@ -114,17 +155,25 @@ class ChromiumLinuxPort(chromium.ChromiumPort):
def _path_to_lighttpd_php(self):
return "/usr/bin/php-cgi"
- def _path_to_driver(self):
- return self._build_path('test_shell')
+ def _path_to_driver(self, configuration=None):
+ if not configuration:
+ configuration = self._options.configuration
+ return self._build_path(configuration, 'test_shell')
def _path_to_helper(self):
return None
def _path_to_image_diff(self):
- return self._build_path('image_diff')
+ return self._build_path(self._options.configuration, 'image_diff')
def _path_to_wdiff(self):
- return 'wdiff'
+ if self._is_redhat_based():
+ return '/usr/bin/dwdiff'
+ else:
+ return '/usr/bin/wdiff'
+
+ def _is_redhat_based(self):
+ return os.path.exists(os.path.join('/etc', 'redhat-release'))
def _shut_down_http_server(self, server_pid):
"""Shut down the lighttpd web server. Blocks until it's fully
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py
index bcffcf8..d5e1757 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py
@@ -29,6 +29,7 @@
"""Chromium Mac implementation of the Port interface."""
+import logging
import os
import platform
import signal
@@ -36,6 +37,8 @@ import subprocess
import chromium
+_log = logging.getLogger("webkitpy.layout_tests.port.chromium_mac")
+
class ChromiumMacPort(chromium.ChromiumPort):
"""Chromium Mac implementation of the Port class."""
@@ -43,22 +46,31 @@ class ChromiumMacPort(chromium.ChromiumPort):
def __init__(self, port_name=None, options=None):
if port_name is None:
port_name = 'chromium-mac'
- if options and not hasattr(options, 'target'):
- options.target = 'Release'
+ if options and not hasattr(options, 'configuration'):
+ options.configuration = 'Release'
chromium.ChromiumPort.__init__(self, port_name, options)
def baseline_search_path(self):
- return [self.baseline_path(),
+ return [self._webkit_baseline_path('chromium-mac'),
+ self._webkit_baseline_path('chromium'),
self._webkit_baseline_path('mac' + self.version()),
self._webkit_baseline_path('mac')]
- def check_sys_deps(self):
- # We have no specific platform dependencies.
- return True
-
- def num_cores(self):
- return int(subprocess.Popen(['sysctl','-n','hw.ncpu'],
- stdout=subprocess.PIPE).stdout.read())
+ def check_build(self, needs_http):
+ result = chromium.ChromiumPort.check_build(self, needs_http)
+ result = self._check_wdiff_install() and result
+ if not result:
+ _log.error('For complete Mac build requirements, please see:')
+ _log.error('')
+ _log.error(' http://code.google.com/p/chromium/wiki/'
+ 'MacBuildInstructions')
+ return result
+
+ def driver_name(self):
+ """name for this port's equivalent of DumpRenderTree."""
+ if self._options.use_drt:
+ return "DumpRenderTree"
+ return "TestShell"
def test_platform_name(self):
# We use 'mac' instead of 'chromium-mac'
@@ -81,21 +93,27 @@ class ChromiumMacPort(chromium.ChromiumPort):
#
def _build_path(self, *comps):
- return self.path_from_chromium_base('xcodebuild', self._options.target,
- *comps)
+ if self._options.use_drt:
+ return self.path_from_webkit_base('WebKit', 'chromium',
+ 'xcodebuild', *comps)
+ return self.path_from_chromium_base('xcodebuild', *comps)
+
+ def _check_wdiff_install(self):
+ f = open(os.devnull, 'w')
+ rcode = 0
+ try:
+ rcode = subprocess.call(['wdiff'], stderr=f)
+ except OSError:
+ _log.warning('wdiff not found. Install using MacPorts or some '
+ 'other means')
+ pass
+ f.close()
+ return True
def _lighttpd_path(self, *comps):
return self.path_from_chromium_base('third_party', 'lighttpd',
'mac', *comps)
- def _kill_process(self, pid):
- """Forcefully kill the process.
-
- Args:
- pid: The id of the process to be killed.
- """
- os.kill(pid, signal.SIGKILL)
-
def _kill_all_process(self, process_name):
"""Kill any processes running under this name."""
# On Mac OS X 10.6, killall has a new constraint: -SIGNALNAME or
@@ -116,25 +134,33 @@ class ChromiumMacPort(chromium.ChromiumPort):
'apache2-httpd.conf')
def _path_to_lighttpd(self):
- return self._lighttp_path('bin', 'lighttp')
+ return self._lighttpd_path('bin', 'lighttpd')
def _path_to_lighttpd_modules(self):
- return self._lighttp_path('lib')
+ return self._lighttpd_path('lib')
def _path_to_lighttpd_php(self):
return self._lighttpd_path('bin', 'php-cgi')
- def _path_to_driver(self):
- # TODO(pinkerton): make |target| happy with case-sensitive file
+ def _path_to_driver(self, configuration=None):
+ # FIXME: make |configuration| happy with case-sensitive file
# systems.
- return self._build_path('TestShell.app', 'Contents', 'MacOS',
- 'TestShell')
+ if not configuration:
+ configuration = self._options.configuration
+ return self._build_path(configuration, self.driver_name() + '.app',
+ 'Contents', 'MacOS', self.driver_name())
def _path_to_helper(self):
- return self._build_path('layout_test_helper')
+ binary_name = 'layout_test_helper'
+ if self._options.use_drt:
+ binary_name = 'LayoutTestHelper'
+ return self._build_path(self._options.configuration, binary_name)
def _path_to_image_diff(self):
- return self._build_path('image_diff')
+ binary_name = 'image_diff'
+ if self._options.use_drt:
+ binary_name = 'ImageDiff'
+ return self._build_path(self._options.configuration, binary_name)
def _path_to_wdiff(self):
return 'wdiff'
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py
index 5eb0ba1..2e3de85 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py
@@ -29,6 +29,7 @@
"""Chromium Win implementation of the Port interface."""
+import logging
import os
import platform
import signal
@@ -37,6 +38,8 @@ import sys
import chromium
+_log = logging.getLogger("webkitpy.layout_tests.port.chromium_win")
+
class ChromiumWinPort(chromium.ChromiumPort):
"""Chromium Win implementation of the Port class."""
@@ -44,33 +47,37 @@ class ChromiumWinPort(chromium.ChromiumPort):
def __init__(self, port_name=None, options=None):
if port_name is None:
port_name = 'chromium-win' + self.version()
- if options and not hasattr(options, 'target'):
- options.target = 'Release'
+ if options and not hasattr(options, 'configuration'):
+ options.configuration = 'Release'
chromium.ChromiumPort.__init__(self, port_name, options)
def baseline_search_path(self):
dirs = []
if self._name == 'chromium-win-xp':
- dirs.append(self._chromium_baseline_path(self._name))
+ dirs.append(self._webkit_baseline_path('chromium-win-xp'))
if self._name in ('chromium-win-xp', 'chromium-win-vista'):
- dirs.append(self._chromium_baseline_path('chromium-win-vista'))
- dirs.append(self._chromium_baseline_path('chromium-win'))
+ dirs.append(self._webkit_baseline_path('chromium-win-vista'))
+ dirs.append(self._webkit_baseline_path('chromium-win'))
+ dirs.append(self._webkit_baseline_path('chromium'))
dirs.append(self._webkit_baseline_path('win'))
dirs.append(self._webkit_baseline_path('mac'))
return dirs
- def check_sys_deps(self):
- # TODO(dpranke): implement this
- return True
+ def check_build(self, needs_http):
+ result = chromium.ChromiumPort.check_build(self, needs_http)
+ if not result:
+ _log.error('For complete Windows build requirements, please '
+ 'see:')
+ _log.error('')
+ _log.error(' http://dev.chromium.org/developers/how-tos/'
+ 'build-instructions-windows')
+ return result
def get_absolute_path(self, filename):
"""Return the absolute path in unix format for the given filename."""
abspath = os.path.abspath(filename)
return abspath.replace('\\', '/')
- def num_cores(self):
- return int(os.environ.get('NUMBER_OF_PROCESSORS', 1))
-
def relative_test_filename(self, filename):
path = filename[len(self.layout_tests_dir()) + 1:]
return path.replace('\\', '/')
@@ -80,6 +87,8 @@ class ChromiumWinPort(chromium.ChromiumPort):
return 'win' + self.version()
def version(self):
+ if not hasattr(sys, 'getwindowsversion'):
+ return ''
winver = sys.getwindowsversion()
if winver[0] == 6 and (winver[1] == 1):
return '-7'
@@ -94,24 +103,15 @@ class ChromiumWinPort(chromium.ChromiumPort):
#
def _build_path(self, *comps):
- # FIXME(dpranke): allow for builds under 'chrome' as well.
- return self.path_from_chromium_base('webkit', self._options.target,
- *comps)
+ p = self.path_from_chromium_base('webkit', *comps)
+ if os.path.exists(p):
+ return p
+ return self.path_from_chromium_base('chrome', *comps)
def _lighttpd_path(self, *comps):
return self.path_from_chromium_base('third_party', 'lighttpd', 'win',
*comps)
- def _kill_process(self, pid):
- """Forcefully kill the process.
-
- Args:
- pid: The id of the process to be killed.
- """
- subprocess.call(('taskkill.exe', '/f', '/pid', str(pid)),
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-
def _path_to_apache(self):
return self.path_from_chromium_base('third_party', 'cygwin', 'usr',
'sbin', 'httpd')
@@ -129,14 +129,16 @@ class ChromiumWinPort(chromium.ChromiumPort):
def _path_to_lighttpd_php(self):
return self._lighttpd_path('php5', 'php-cgi.exe')
- def _path_to_driver(self):
- return self._build_path('test_shell.exe')
+ def _path_to_driver(self, configuration=None):
+ if not configuration:
+ configuration = self._options.configuration
+ return self._build_path(configuration, 'test_shell.exe')
def _path_to_helper(self):
- return self._build_path('layout_test_helper.exe')
+ return self._build_path(self._options.configuration, 'layout_test_helper.exe')
def _path_to_image_diff(self):
- return self._build_path('image_diff.exe')
+ return self._build_path(self._options.configuration, 'image_diff.exe')
def _path_to_wdiff(self):
return self.path_from_chromium_base('third_party', 'cygwin', 'bin',
@@ -150,8 +152,10 @@ class ChromiumWinPort(chromium.ChromiumPort):
server_pid: The process ID of the running server.
"""
subprocess.Popen(('taskkill.exe', '/f', '/im', 'LightTPD.exe'),
+ stdin=open(os.devnull, 'r'),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).wait()
subprocess.Popen(('taskkill.exe', '/f', '/im', 'httpd.exe'),
+ stdin=open(os.devnull, 'r'),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).wait()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
new file mode 100644
index 0000000..7a6717f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 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 Google name nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""This is a test implementation of the Port interface that generates the
+ correct output for every test. It can be used for perf testing, because
+ it is pretty much a lower limit on how fast a port can possibly run.
+
+ This implementation acts as a wrapper around a real port (the real port
+ is held as a delegate object). To specify which port, use the port name
+ 'dryrun-XXX' (e.g., 'dryrun-chromium-mac-leopard'). If you use just
+ 'dryrun', it uses the default port.
+
+ Note that because this is really acting as a wrapper around the underlying
+ port, you must be able to run the underlying port as well
+ (check_build() and check_sys_deps() must pass and auxiliary binaries
+ like layout_test_helper and httpd must work).
+
+ This implementation also modifies the test expectations so that all
+ tests are either SKIPPED or expected to PASS."""
+
+from __future__ import with_statement
+
+import base
+import factory
+
+
+def _read_file(path, mode='r'):
+ """Return the contents of a file as a string.
+
+ Returns '' if anything goes wrong, instead of throwing an IOError.
+
+ """
+ contents = ''
+ try:
+ with open(path, mode) as f:
+ contents = f.read()
+ except IOError:
+ pass
+ return contents
+
+
+def _write_file(path, contents, mode='w'):
+ """Write the string to the specified path.
+
+ Returns nothing if the write fails, instead of raising an IOError.
+
+ """
+ try:
+ with open(path, mode) as f:
+ f.write(contents)
+ except IOError:
+ pass
+
+
+class DryRunPort(object):
+ """DryRun implementation of the Port interface."""
+
+ def __init__(self, port_name=None, options=None):
+ pfx = 'dryrun-'
+ if port_name.startswith(pfx):
+ port_name = port_name[len(pfx):]
+ else:
+ port_name = None
+ self.__delegate = factory.get(port_name, options)
+
+ def __getattr__(self, name):
+ return getattr(self.__delegate, name)
+
+ def check_build(self, needs_http):
+ return True
+
+ def check_sys_deps(self, needs_http):
+ return True
+
+ def start_helper(self):
+ pass
+
+ def start_http_server(self):
+ pass
+
+ def start_websocket_server(self):
+ pass
+
+ def stop_helper(self):
+ pass
+
+ def stop_http_server(self):
+ pass
+
+ def stop_websocket_server(self):
+ pass
+
+ def start_driver(self, image_path, options):
+ return DryrunDriver(self, image_path, options)
+
+
+class DryrunDriver(base.Driver):
+ """Dryrun implementation of the DumpRenderTree / Driver interface."""
+
+ def __init__(self, port, image_path, test_driver_options):
+ self._port = port
+ self._driver_options = test_driver_options
+ self._image_path = image_path
+ self._layout_tests_dir = None
+
+ def poll(self):
+ return None
+
+ def returncode(self):
+ return 0
+
+ def run_test(self, uri, timeoutms, image_hash):
+ test_name = self._uri_to_test(uri)
+
+ text_filename = self._port.expected_filename(test_name, '.txt')
+ text_output = _read_file(text_filename)
+
+ if image_hash:
+ image_filename = self._port.expected_filename(test_name, '.png')
+ image = _read_file(image_filename, 'rb')
+ if self._image_path:
+ _write_file(self._image_path, image)
+ hash_filename = self._port.expected_filename(test_name,
+ '.checksum')
+ hash = _read_file(hash_filename)
+ else:
+ hash = None
+ return (False, False, hash, text_output, None)
+
+ def stop(self):
+ pass
+
+ def _uri_to_test(self, uri):
+ """Return the base layout test name for a given URI.
+
+ This returns the test name for a given URI, e.g., if you passed in
+ "file:///src/LayoutTests/fast/html/keygen.html" it would return
+ "fast/html/keygen.html".
+
+ """
+ if not self._layout_tests_dir:
+ self._layout_tests_dir = self._port.layout_tests_dir()
+ test = uri
+
+ if uri.startswith("file:///"):
+ test = test.replace('file://', '')
+ return test
+ elif uri.startswith("http://127.0.0.1:8880/"):
+ # websocket tests
+ test = test.replace('http://127.0.0.1:8880/',
+ self._layout_tests_dir + '/')
+ return test
+ elif uri.startswith("http://"):
+ # regular HTTP test
+ test = test.replace('http://127.0.0.1:8000/',
+ self._layout_tests_dir + '/http/tests/')
+ return test
+ elif uri.startswith("https://"):
+ test = test.replace('https://127.0.0.1:8443/',
+ self._layout_tests_dir + '/http/tests/')
+ return test
+ else:
+ raise NotImplementedError('unknown url type: %s' % uri)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py
new file mode 100644
index 0000000..95b90da
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 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.
+
+"""Factory method to retrieve the appropriate port implementation."""
+
+
+import sys
+
+
+def get(port_name=None, options=None):
+ """Returns an object implementing the Port interface. If
+ port_name is None, this routine attempts to guess at the most
+ appropriate port on this platform."""
+ port_to_use = port_name
+ if port_to_use is None:
+ if sys.platform == 'win32' or sys.platform == 'cygwin':
+ if options and hasattr(options, 'chromium') and options.chromium:
+ port_to_use = 'chromium-win'
+ else:
+ port_to_use = 'win'
+ elif sys.platform == 'linux2':
+ port_to_use = 'chromium-linux'
+ elif sys.platform == 'darwin':
+ if options and hasattr(options, 'chromium') and options.chromium:
+ port_to_use = 'chromium-mac'
+ else:
+ port_to_use = 'mac'
+
+ if port_to_use is None:
+ raise NotImplementedError('unknown port; sys.platform = "%s"' %
+ sys.platform)
+
+ if port_to_use == 'test':
+ import test
+ return test.TestPort(port_name, options)
+ elif port_to_use.startswith('dryrun'):
+ import dryrun
+ return dryrun.DryRunPort(port_name, options)
+ elif port_to_use.startswith('mac'):
+ import mac
+ return mac.MacPort(port_name, options)
+ elif port_to_use.startswith('win'):
+ import win
+ return win.WinPort(port_name, options)
+ elif port_to_use.startswith('gtk'):
+ import gtk
+ return gtk.GtkPort(port_name, options)
+ elif port_to_use.startswith('qt'):
+ import qt
+ return qt.QtPort(port_name, options)
+ elif port_to_use.startswith('chromium-mac'):
+ import chromium_mac
+ return chromium_mac.ChromiumMacPort(port_name, options)
+ elif port_to_use.startswith('chromium-linux'):
+ import chromium_linux
+ return chromium_linux.ChromiumLinuxPort(port_name, options)
+ elif port_to_use.startswith('chromium-win'):
+ import chromium_win
+ return chromium_win.ChromiumWinPort(port_name, options)
+
+ raise NotImplementedError('unsupported port: %s' % port_to_use)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py
new file mode 100644
index 0000000..de5e28a
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py
@@ -0,0 +1,91 @@
+# Copyright (C) 2010 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 Google name 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.
+
+"""WebKit Gtk implementation of the Port interface."""
+
+import logging
+import os
+import subprocess
+
+from webkitpy.layout_tests.port.webkit import WebKitPort
+
+_log = logging.getLogger("webkitpy.layout_tests.port.gtk")
+
+
+class GtkPort(WebKitPort):
+ """WebKit Gtk implementation of the Port class."""
+
+ def __init__(self, port_name=None, options=None):
+ if port_name is None:
+ port_name = 'gtk'
+ WebKitPort.__init__(self, port_name, options)
+
+ def _tests_for_other_platforms(self):
+ # FIXME: This list could be dynamic based on platform name and
+ # pushed into base.Port.
+ # This really need to be automated.
+ return [
+ "platform/chromium",
+ "platform/win",
+ "platform/qt",
+ "platform/mac",
+ ]
+
+ def _path_to_apache_config_file(self):
+ # FIXME: This needs to detect the distribution and change config files.
+ return os.path.join(self.layout_tests_dir(), 'http', 'conf',
+ 'apache2-debian-httpd.conf')
+
+ def _kill_all_process(self, process_name):
+ null = open(os.devnull)
+ subprocess.call(['killall', '-TERM', '-u', os.getenv('USER'),
+ process_name], stderr=null)
+ null.close()
+
+ def _shut_down_http_server(self, server_pid):
+ """Shut down the httpd web server. Blocks until it's fully
+ shut down.
+
+ Args:
+ server_pid: The process ID of the running server.
+ """
+ # server_pid is not set when "http_server.py stop" is run manually.
+ if server_pid is None:
+ # FIXME: This isn't ideal, since it could conflict with
+ # lighttpd processes not started by http_server.py,
+ # but good enough for now.
+ self._kill_all_process('apache2')
+ else:
+ try:
+ os.kill(server_pid, signal.SIGTERM)
+ # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
+ except OSError:
+ # Sometimes we get a bad PID (e.g. from a stale httpd.pid
+ # file), so if kill fails on the given PID, just try to
+ # 'killall' web servers.
+ self._shut_down_http_server(None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py
index 0315704..cc434bc 100755
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py
@@ -40,8 +40,11 @@ import tempfile
import time
import urllib
+import factory
import http_server_base
+_log = logging.getLogger("webkitpy.layout_tests.port.http_server")
+
class HttpdNotStarted(Exception):
pass
@@ -200,11 +203,11 @@ class Lighttpd(http_server_base.HttpServerBase):
env['PATH'])
if sys.platform == 'win32' and self._register_cygwin:
- setup_mount = port.path_from_chromium_base('third_party',
+ setup_mount = self._port_obj.path_from_chromium_base('third_party',
'cygwin', 'setup_mount.bat')
subprocess.Popen(setup_mount).wait()
- logging.debug('Starting http server')
+ _log.debug('Starting http server')
self._process = subprocess.Popen(start_cmd, env=env)
# Wait for server to start.
@@ -216,7 +219,7 @@ class Lighttpd(http_server_base.HttpServerBase):
if not server_started or self._process.returncode != None:
raise google.httpd_utils.HttpdNotStarted('Failed to start httpd.')
- logging.debug("Server successfully started")
+ _log.debug("Server successfully started")
# TODO(deanm): Find a nicer way to shutdown cleanly. Our log files are
# probably not being flushed, etc... why doesn't our python have os.kill ?
@@ -233,40 +236,3 @@ class Lighttpd(http_server_base.HttpServerBase):
if self._process:
self._process.wait()
self._process = None
-
-if '__main__' == __name__:
- # Provide some command line params for starting/stopping the http server
- # manually. Also used in ui_tests to run http layout tests in a browser.
- option_parser = optparse.OptionParser()
- option_parser.add_option('-k', '--server',
- help='Server action (start|stop)')
- option_parser.add_option('-p', '--port',
- help='Port to listen on (overrides layout test ports)')
- option_parser.add_option('-r', '--root',
- help='Absolute path to DocumentRoot (overrides layout test roots)')
- option_parser.add_option('--register_cygwin', action="store_true",
- dest="register_cygwin", help='Register Cygwin paths (on Win try bots)')
- option_parser.add_option('--run_background', action="store_true",
- dest="run_background",
- help='Run on background (for running as UI test)')
- options, args = option_parser.parse_args()
-
- if not options.server:
- print ('Usage: %s --server {start|stop} [--root=root_dir]'
- ' [--port=port_number]' % sys.argv[0])
- else:
- if (options.root is None) and (options.port is not None):
- # specifying root but not port means we want httpd on default
- # set of ports that LayoutTest use, but pointing to a different
- # source of tests. Specifying port but no root does not seem
- # meaningful.
- raise 'Specifying port requires also a root.'
- httpd = Lighttpd(tempfile.gettempdir(),
- port=options.port,
- root=options.root,
- register_cygwin=options.register_cygwin,
- run_background=options.run_background)
- if 'start' == options.server:
- httpd.start()
- else:
- httpd.stop(force=True)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py
index e82943e..c9805d6 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py
@@ -34,6 +34,8 @@ import os
import time
import urllib
+_log = logging.getLogger("webkitpy.layout_tests.port.http_server_base")
+
class HttpServerBase(object):
@@ -47,6 +49,7 @@ class HttpServerBase(object):
while time.time() - start_time < 20:
if action():
return True
+ _log.debug("Waiting for action: %s" % action)
time.sleep(1)
return False
@@ -63,9 +66,9 @@ class HttpServerBase(object):
try:
response = urllib.urlopen(url)
- logging.debug("Server running at %s" % url)
+ _log.debug("Server running at %s" % url)
except IOError:
- logging.debug("Server NOT running at %s" % url)
+ _log.debug("Server NOT running at %s" % url)
return False
return True
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf b/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf
index d3150dd..2e9c82e 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf
@@ -21,6 +21,7 @@ mimetype.assign = (
".html" => "text/html",
".htm" => "text/html",
".xhtml" => "application/xhtml+xml",
+ ".xhtmlmp" => "application/vnd.wap.xhtml+xml",
".js" => "text/javascript",
".log" => "text/plain",
".conf" => "text/plain",
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py
index d355f62..cf4daa8 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# Copyright (C) 2010 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -29,31 +28,41 @@
"""WebKit Mac implementation of the Port interface."""
-import fcntl
import logging
import os
import pdb
import platform
-import select
+import re
+import shutil
import signal
import subprocess
import sys
import time
import webbrowser
-import base
+import webkitpy.common.system.ospath as ospath
+import webkitpy.layout_tests.port.server_process as server_process
+from webkitpy.layout_tests.port.webkit import WebKitPort, WebKitDriver
-import webkitpy
-from webkitpy import executive
+_log = logging.getLogger("webkitpy.layout_tests.port.mac")
-class MacPort(base.Port):
+
+class MacPort(WebKitPort):
"""WebKit Mac implementation of the Port class."""
def __init__(self, port_name=None, options=None):
if port_name is None:
port_name = 'mac' + self.version()
- base.Port.__init__(self, port_name, options)
- self._cached_build_root = None
+ WebKitPort.__init__(self, port_name, options)
+
+ def default_child_processes(self):
+ # FIXME: new-run-webkit-tests is unstable on Mac running more than
+ # four threads in parallel.
+ # See https://bugs.webkit.org/show_bug.cgi?id=36622
+ child_processes = WebKitPort.default_child_processes(self)
+ if child_processes > 4:
+ return 4
+ return child_processes
def baseline_search_path(self):
dirs = []
@@ -66,53 +75,13 @@ class MacPort(base.Port):
dirs.append(self._webkit_baseline_path('mac'))
return dirs
- def check_sys_deps(self):
- if executive.run_command([self.script_path("build-dumprendertree")], return_exit_code=True) != 0:
- return False
-
- driver_path = self._path_to_driver()
- if not os.path.exists(driver_path):
- logging.error("DumpRenderTree was not found at %s" % driver_path)
- return False
-
- # This should also validate that the ImageDiff path is valid (once this script knows how to use ImageDiff).
- # https://bugs.webkit.org/show_bug.cgi?id=34826
- return True
-
- def num_cores(self):
- return int(os.popen2("sysctl -n hw.ncpu")[1].read())
-
- def results_directory(self):
- return ('/tmp/run-chromium-webkit-tests-' +
- self._options.results_directory)
-
- def setup_test_run(self):
- # This port doesn't require any specific configuration.
- pass
-
- def show_results_html_file(self, results_filename):
- uri = self.filename_to_uri(results_filename)
- webbrowser.open(uri, new=1)
-
- def start_driver(self, image_path, options):
- """Starts a new Driver and returns a handle to it."""
- return MacDriver(self, image_path, options)
-
- def start_helper(self):
- # This port doesn't use a helper process.
- pass
-
- def stop_helper(self):
- # This port doesn't use a helper process.
- pass
-
- def test_base_platform_names(self):
- # At the moment we don't use test platform names, but we have
- # to return something.
- return ('mac',)
+ def path_to_test_expectations_file(self):
+ return self.path_from_webkit_base('LayoutTests', 'platform',
+ 'mac', 'test_expectations.txt')
def _skipped_file_paths(self):
- # FIXME: This method will need to be made work for non-mac platforms and moved into base.Port.
+ # FIXME: This method will need to be made work for non-mac
+ # platforms and moved into base.Port.
skipped_files = []
if self._name in ('mac-tiger', 'mac-leopard', 'mac-snowleopard'):
skipped_files.append(os.path.join(
@@ -121,79 +90,8 @@ class MacPort(base.Port):
'Skipped'))
return skipped_files
- def _tests_for_other_platforms(self):
- # The original run-webkit-tests builds up a "whitelist" of tests to run, and passes that to DumpRenderTree.
- # run-chromium-webkit-tests assumes we run *all* tests and test_expectations.txt functions as a blacklist.
- # FIXME: This list could be dynamic based on platform name and pushed into base.Port.
- return [
- "platform/chromium",
- "platform/gtk",
- "platform/qt",
- "platform/win",
- ]
-
- def _tests_for_disabled_features(self):
- # FIXME: This should use the feature detection from webkitperl/features.pm to match run-webkit-tests.
- # For now we hard-code a list of features known to be disabled on the Mac platform.
- disabled_feature_tests = [
- "fast/xhtmlmp",
- "http/tests/wml",
- "mathml",
- "wml",
- ]
- # FIXME: webarchive tests expect to read-write from -expected.webarchive files instead of .txt files.
- # This script doesn't know how to do that yet, so pretend they're just "disabled".
- webarchive_tests = [
- "webarchive",
- "svg/webarchive",
- "http/tests/webarchive",
- "svg/custom/image-with-prefix-in-webarchive.svg",
- ]
- return disabled_feature_tests + webarchive_tests
-
- def _tests_from_skipped_file(self, skipped_file):
- tests_to_skip = []
- for line in skipped_file.readlines():
- line = line.strip()
- if line.startswith('#') or not len(line):
- continue
- tests_to_skip.append(line)
- return tests_to_skip
-
- def _expectations_from_skipped_files(self):
- tests_to_skip = []
- for filename in self._skipped_file_paths():
- if not os.path.exists(filename):
- logging.warn("Failed to open Skipped file: %s" % filename)
- continue
- skipped_file = file(filename)
- tests_to_skip.extend(self._tests_from_skipped_file(skipped_file))
- skipped_file.close()
- return tests_to_skip
-
- def test_expectations(self):
- # The WebKit mac port uses 'Skipped' files at the moment. Each
- # file contains a list of files or directories to be skipped during
- # the test run. The total list of tests to skipped is given by the
- # contents of the generic Skipped file found in platform/X plus
- # a version-specific file found in platform/X-version. Duplicate
- # entries are allowed. This routine reads those files and turns
- # contents into the format expected by test_expectations.
- tests_to_skip = set(self._expectations_from_skipped_files()) # Use a set to allow duplicates
- tests_to_skip.update(self._tests_for_other_platforms())
- tests_to_skip.update(self._tests_for_disabled_features())
- expectations = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" % test_path, tests_to_skip)
- return "\n".join(expectations)
-
def test_platform_name(self):
- # At the moment we don't use test platform names, but we have
- # to return something.
- return 'mac'
-
- def test_platform_names(self):
- # At the moment we don't use test platform names, but we have
- # to return something.
- return ('mac',)
+ return 'mac' + self.version()
def version(self):
os_version_string = platform.mac_ver()[0] # e.g. "10.5.6"
@@ -208,23 +106,32 @@ class MacPort(base.Port):
return '-snowleopard'
return ''
- #
- # PROTECTED METHODS
- #
-
- def _build_path(self, *comps):
- if not self._cached_build_root:
- self._cached_build_root = executive.run_command([self.script_path("webkit-build-directory"), "--top-level"]).rstrip()
- return os.path.join(self._cached_build_root, self._options.target, *comps)
+ def _build_java_test_support(self):
+ java_tests_path = os.path.join(self.layout_tests_dir(), "java")
+ build_java = ["/usr/bin/make", "-C", java_tests_path]
+ if self._executive.run_command(build_java, return_exit_code=True):
+ _log.error("Failed to build Java support files: %s" % build_java)
+ return False
+ return True
- def _kill_process(self, pid):
- """Forcefully kill the process.
+ def _check_port_build(self):
+ return self._build_java_test_support()
- Args:
- pid: The id of the process to be killed.
- """
- os.kill(pid, signal.SIGKILL)
+ def _tests_for_other_platforms(self):
+ # The original run-webkit-tests builds up a "whitelist" of tests to
+ # run, and passes that to DumpRenderTree. new-run-webkit-tests assumes
+ # we run *all* tests and test_expectations.txt functions as a
+ # blacklist.
+ # FIXME: This list could be dynamic based on platform name and
+ # pushed into base.Port.
+ return [
+ "platform/chromium",
+ "platform/gtk",
+ "platform/qt",
+ "platform/win",
+ ]
+ # FIXME: This doesn't have anything to do with WebKit.
def _kill_all_process(self, process_name):
# On Mac OS X 10.6, killall has a new constraint: -SIGNALNAME or
# -SIGNALNUMBER must come first. Example problem:
@@ -236,25 +143,11 @@ class MacPort(base.Port):
process_name], stderr=null)
null.close()
- def _path_to_apache(self):
- return '/usr/sbin/httpd'
-
def _path_to_apache_config_file(self):
return os.path.join(self.layout_tests_dir(), 'http', 'conf',
'apache2-httpd.conf')
- def _path_to_driver(self):
- return self._build_path('DumpRenderTree')
-
- def _path_to_helper(self):
- return None
-
- def _path_to_image_diff(self):
- return self._build_path('image_diff') # FIXME: This is wrong and should be "ImageDiff", but having the correct path causes other parts of the script to hang.
-
- def _path_to_wdiff(self):
- return 'wdiff' # FIXME: This does not exist on a default Mac OS X Leopard install.
-
+ # FIXME: This doesn't have anything to do with WebKit.
def _shut_down_http_server(self, server_pid):
"""Shut down the lighttpd web server. Blocks until it's fully
shut down.
@@ -264,209 +157,16 @@ class MacPort(base.Port):
"""
# server_pid is not set when "http_server.py stop" is run manually.
if server_pid is None:
- # TODO(mmoss) This isn't ideal, since it could conflict with
+ # FIXME: This isn't ideal, since it could conflict with
# lighttpd processes not started by http_server.py,
# but good enough for now.
self._kill_all_process('httpd')
else:
try:
os.kill(server_pid, signal.SIGTERM)
- # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
+ # FIXME: Maybe throw in a SIGKILL just to be sure?
except OSError:
# Sometimes we get a bad PID (e.g. from a stale httpd.pid
# file), so if kill fails on the given PID, just try to
# 'killall' web servers.
self._shut_down_http_server(None)
-
-
-class MacDriver(base.Driver):
- """implementation of the DumpRenderTree interface."""
-
- def __init__(self, port, image_path, driver_options):
- self._port = port
- self._driver_options = driver_options
- self._target = port._options.target
- self._image_path = image_path
- self._stdout_fd = None
- self._cmd = None
- self._env = None
- self._proc = None
- self._read_buffer = ''
-
- cmd = []
- # Hook for injecting valgrind or other runtime instrumentation,
- # used by e.g. tools/valgrind/valgrind_tests.py.
- wrapper = os.environ.get("BROWSER_WRAPPER", None)
- if wrapper != None:
- cmd += [wrapper]
- if self._port._options.wrapper:
- # This split() isn't really what we want -- it incorrectly will
- # split quoted strings within the wrapper argument -- but in
- # practice it shouldn't come up and the --help output warns
- # about it anyway.
- cmd += self._options.wrapper.split()
- # FIXME: Using arch here masks any possible file-not-found errors from a non-existant driver executable.
- cmd += ['arch', '-i386', port._path_to_driver(), '-']
-
- # FIXME: This is a hack around our lack of ImageDiff support for now.
- if not self._port._options.no_pixel_tests:
- logging.warn("This port does not yet support pixel tests.")
- self._port._options.no_pixel_tests = True
- #cmd.append('--pixel-tests')
-
- #if driver_options:
- # cmd += driver_options
- env = os.environ
- env['DYLD_FRAMEWORK_PATH'] = self._port._build_path()
- self._cmd = cmd
- self._env = env
- self.restart()
-
- def poll(self):
- return self._proc.poll()
-
- def restart(self):
- self.stop()
- self._proc = subprocess.Popen(self._cmd, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=self._env)
-
- def returncode(self):
- return self._proc.returncode
-
- def run_test(self, uri, timeoutms, image_hash):
- output = []
- error = []
- image = ''
- crash = False
- timeout = False
- actual_uri = None
- actual_image_hash = None
-
- if uri.startswith("file:///"):
- cmd = uri[7:]
- else:
- cmd = uri
-
- if image_hash:
- cmd += "'" + image_hash
- cmd += "\n"
-
- self._proc.stdin.write(cmd)
- self._stdout_fd = self._proc.stdout.fileno()
- fl = fcntl.fcntl(self._stdout_fd, fcntl.F_GETFL)
- fcntl.fcntl(self._stdout_fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
-
- stop_time = time.time() + (int(timeoutms) / 1000.0)
- resp = ''
- (timeout, line) = self._read_line(timeout, stop_time)
- resp += line
- have_seen_content_type = False
- while not timeout and line.rstrip() != "#EOF":
- # Make sure we haven't crashed.
- if line == '' and self.poll() is not None:
- # This is hex code 0xc000001d, which is used for abrupt
- # termination. This happens if we hit ctrl+c from the prompt
- # and we happen to be waiting on the test_shell.
- # sdoyon: Not sure for which OS and in what circumstances the
- # above code is valid. What works for me under Linux to detect
- # ctrl+c is for the subprocess returncode to be negative
- # SIGINT. And that agrees with the subprocess documentation.
- if (-1073741510 == self.returncode() or
- - signal.SIGINT == self.returncode()):
- raise KeyboardInterrupt
- crash = True
- break
-
- elif (line.startswith('Content-Type:') and not
- have_seen_content_type):
- have_seen_content_type = True
- pass
- else:
- output.append(line)
-
- (timeout, line) = self._read_line(timeout, stop_time)
- resp += line
-
- # Now read a second block of text for the optional image data
- image_length = 0
- (timeout, line) = self._read_line(timeout, stop_time)
- resp += line
- HASH_HEADER = 'ActualHash: '
- LENGTH_HEADER = 'Content-Length: '
- while not timeout and not crash and line.rstrip() != "#EOF":
- if line == '' and self.poll() is not None:
- if (-1073741510 == self.returncode() or
- - signal.SIGINT == self.returncode()):
- raise KeyboardInterrupt
- crash = True
- break
- elif line.startswith(HASH_HEADER):
- actual_image_hash = line[len(HASH_HEADER):].strip()
- elif line.startswith('Content-Type:'):
- pass
- elif line.startswith(LENGTH_HEADER):
- image_length = int(line[len(LENGTH_HEADER):])
- elif image_length:
- image += line
-
- (timeout, line) = self._read_line(timeout, stop_time, image_length)
- resp += line
-
- if timeout:
- self.restart()
-
- if self._image_path and len(self._image_path):
- image_file = file(self._image_path, "wb")
- image_file.write(image)
- image_file.close()
-
- return (crash, timeout, actual_image_hash,
- ''.join(output), ''.join(error))
-
- def stop(self):
- if self._proc:
- self._proc.stdin.close()
- self._proc.stdout.close()
- if self._proc.stderr:
- self._proc.stderr.close()
- if (sys.platform not in ('win32', 'cygwin') and
- not self._proc.poll()):
- # Closing stdin/stdout/stderr hangs sometimes on OS X.
- null = open(os.devnull, "w")
- subprocess.Popen(["kill", "-9",
- str(self._proc.pid)], stderr=null)
- null.close()
-
- def _read_line(self, timeout, stop_time, image_length=0):
- now = time.time()
- read_fds = []
-
- # first check to see if we have a line already read or if we've
- # read the entire image
- if image_length and len(self._read_buffer) >= image_length:
- out = self._read_buffer[0:image_length]
- self._read_buffer = self._read_buffer[image_length:]
- return (timeout, out)
-
- idx = self._read_buffer.find('\n')
- if not image_length and idx != -1:
- out = self._read_buffer[0:idx + 1]
- self._read_buffer = self._read_buffer[idx + 1:]
- return (timeout, out)
-
- # If we've timed out, return just what we have, if anything
- if timeout or now >= stop_time:
- out = self._read_buffer
- self._read_buffer = ''
- return (True, out)
-
- (read_fds, write_fds, err_fds) = select.select(
- [self._stdout_fd], [], [], stop_time - now)
- try:
- if timeout or len(read_fds) == 1:
- self._read_buffer += self._proc.stdout.read()
- except IOError, e:
- read = []
- return self._read_line(timeout, stop_time)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py
new file mode 100644
index 0000000..67cdefe
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py
@@ -0,0 +1,99 @@
+# Copyright (C) 2010 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 Google name 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.
+
+"""QtWebKit implementation of the Port interface."""
+
+import logging
+import os
+import subprocess
+import signal
+
+from webkitpy.layout_tests.port.webkit import WebKitPort
+
+_log = logging.getLogger("webkitpy.layout_tests.port.qt")
+
+
+class QtPort(WebKitPort):
+ """QtWebKit implementation of the Port class."""
+
+ def __init__(self, port_name=None, options=None):
+ if port_name is None:
+ port_name = 'qt'
+ WebKitPort.__init__(self, port_name, options)
+
+ def _tests_for_other_platforms(self):
+ # FIXME: This list could be dynamic based on platform name and
+ # pushed into base.Port.
+ # This really need to be automated.
+ return [
+ "platform/chromium",
+ "platform/win",
+ "platform/gtk",
+ "platform/mac",
+ ]
+
+ def _path_to_apache_config_file(self):
+ # FIXME: This needs to detect the distribution and change config files.
+ return os.path.join(self.layout_tests_dir(), 'http', 'conf',
+ 'apache2-debian-httpd.conf')
+
+ def _kill_all_process(self, process_name):
+ null = open(os.devnull)
+ subprocess.call(['killall', '-TERM', '-u', os.getenv('USER'),
+ process_name], stderr=null)
+ null.close()
+
+ def _shut_down_http_server(self, server_pid):
+ """Shut down the httpd web server. Blocks until it's fully
+ shut down.
+
+ Args:
+ server_pid: The process ID of the running server.
+ """
+ # server_pid is not set when "http_server.py stop" is run manually.
+ if server_pid is None:
+ # FIXME: This isn't ideal, since it could conflict with
+ # lighttpd processes not started by http_server.py,
+ # but good enough for now.
+ self._kill_all_process('apache2')
+ else:
+ try:
+ os.kill(server_pid, signal.SIGTERM)
+ # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
+ except OSError:
+ # Sometimes we get a bad PID (e.g. from a stale httpd.pid
+ # file), so if kill fails on the given PID, just try to
+ # 'killall' web servers.
+ self._shut_down_http_server(None)
+
+ def _build_driver(self):
+ # The Qt port builds DRT as part of the main build step
+ return True
+
+ def _path_to_driver(self):
+ return self._build_path('bin/DumpRenderTree')
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py
new file mode 100644
index 0000000..f1c6d73
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 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 Google name 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.
+
+"""Package that implements the ServerProcess wrapper class"""
+
+import fcntl
+import logging
+import os
+import select
+import signal
+import subprocess
+import sys
+import time
+
+_log = logging.getLogger("webkitpy.layout_tests.port.server_process")
+
+
+class ServerProcess:
+ """This class provides a wrapper around a subprocess that
+ implements a simple request/response usage model. The primary benefit
+ is that reading responses takes a timeout, so that we don't ever block
+ indefinitely. The class also handles transparently restarting processes
+ as necessary to keep issuing commands."""
+
+ def __init__(self, port_obj, name, cmd, env=None):
+ self._port = port_obj
+ self._name = name
+ self._cmd = cmd
+ self._env = env
+ self._reset()
+
+ def _reset(self):
+ self._proc = None
+ self._output = ''
+ self.crashed = False
+ self.timed_out = False
+ self.error = ''
+
+ def _start(self):
+ if self._proc:
+ raise ValueError("%s already running" % self._name)
+ self._reset()
+ close_fds = sys.platform not in ('win32', 'cygwin')
+ self._proc = subprocess.Popen(self._cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ close_fds=close_fds,
+ env=self._env)
+ fd = self._proc.stdout.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ fd = self._proc.stderr.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+ def handle_interrupt(self):
+ """This routine checks to see if the process crashed or exited
+ because of a keyboard interrupt and raises KeyboardInterrupt
+ accordingly."""
+ if self.crashed:
+ # This is hex code 0xc000001d, which is used for abrupt
+ # termination. This happens if we hit ctrl+c from the prompt
+ # and we happen to be waiting on the DumpRenderTree.
+ # sdoyon: Not sure for which OS and in what circumstances the
+ # above code is valid. What works for me under Linux to detect
+ # ctrl+c is for the subprocess returncode to be negative
+ # SIGINT. And that agrees with the subprocess documentation.
+ if (-1073741510 == self._proc.returncode or
+ - signal.SIGINT == self._proc.returncode):
+ raise KeyboardInterrupt
+ return
+
+ def poll(self):
+ """Check to see if the underlying process is running; returns None
+ if it still is (wrapper around subprocess.poll)."""
+ if self._proc:
+ return self._proc.poll()
+ return None
+
+ def returncode(self):
+ """Returns the exit code from the subprcoess; returns None if the
+ process hasn't exited (this is a wrapper around subprocess.returncode).
+ """
+ if self._proc:
+ return self._proc.returncode
+ return None
+
+ def write(self, input):
+ """Write a request to the subprocess. The subprocess is (re-)start()'ed
+ if is not already running."""
+ if not self._proc:
+ self._start()
+ self._proc.stdin.write(input)
+
+ def read_line(self, timeout):
+ """Read a single line from the subprocess, waiting until the deadline.
+ If the deadline passes, the call times out. Note that even if the
+ subprocess has crashed or the deadline has passed, if there is output
+ pending, it will be returned.
+
+ Args:
+ timeout: floating-point number of seconds the call is allowed
+ to block for. A zero or negative number will attempt to read
+ any existing data, but will not block. There is no way to
+ block indefinitely.
+ Returns:
+ output: data returned, if any. If no data is available and the
+ call times out or crashes, an empty string is returned. Note
+ that the returned string includes the newline ('\n')."""
+ return self._read(timeout, size=0)
+
+ def read(self, timeout, size):
+ """Attempts to read size characters from the subprocess, waiting until
+ the deadline passes. If the deadline passes, any available data will be
+ returned. Note that even if the deadline has passed or if the
+ subprocess has crashed, any available data will still be returned.
+
+ Args:
+ timeout: floating-point number of seconds the call is allowed
+ to block for. A zero or negative number will attempt to read
+ any existing data, but will not block. There is no way to
+ block indefinitely.
+ size: amount of data to read. Must be a postive integer.
+ Returns:
+ output: data returned, if any. If no data is available, an empty
+ string is returned.
+ """
+ if size <= 0:
+ raise ValueError('ServerProcess.read() called with a '
+ 'non-positive size: %d ' % size)
+ return self._read(timeout, size)
+
+ def _read(self, timeout, size):
+ """Internal routine that actually does the read."""
+ index = -1
+ out_fd = self._proc.stdout.fileno()
+ err_fd = self._proc.stderr.fileno()
+ select_fds = (out_fd, err_fd)
+ deadline = time.time() + timeout
+ while not self.timed_out and not self.crashed:
+ if self._proc.poll() != None:
+ self.crashed = True
+ self.handle_interrupt()
+
+ now = time.time()
+ if now > deadline:
+ self.timed_out = True
+
+ # Check to see if we have any output we can return.
+ if size and len(self._output) >= size:
+ index = size
+ elif size == 0:
+ index = self._output.find('\n') + 1
+
+ if index or self.crashed or self.timed_out:
+ output = self._output[0:index]
+ self._output = self._output[index:]
+ return output
+
+ # Nope - wait for more data.
+ (read_fds, write_fds, err_fds) = select.select(select_fds, [],
+ select_fds,
+ deadline - now)
+ try:
+ if out_fd in read_fds:
+ self._output += self._proc.stdout.read()
+ if err_fd in read_fds:
+ self.error += self._proc.stderr.read()
+ except IOError, e:
+ pass
+
+ def stop(self):
+ """Stop (shut down) the subprocess), if it is running."""
+ pid = self._proc.pid
+ self._proc.stdin.close()
+ self._proc.stdout.close()
+ if self._proc.stderr:
+ self._proc.stderr.close()
+ if sys.platform not in ('win32', 'cygwin'):
+ # Closing stdin/stdout/stderr hangs sometimes on OS X,
+ # (see restart(), above), and anyway we don't want to hang
+ # the harness if DumpRenderTree is buggy, so we wait a couple
+ # seconds to give DumpRenderTree a chance to clean up, but then
+ # force-kill the process if necessary.
+ KILL_TIMEOUT = 3.0
+ timeout = time.time() + KILL_TIMEOUT
+ while self._proc.poll() is None and time.time() < timeout:
+ time.sleep(0.1)
+ if self._proc.poll() is None:
+ _log.warning('stopping %s timed out, killing it' %
+ self._name)
+ null = open(os.devnull, "w")
+ subprocess.Popen(["kill", "-9",
+ str(self._proc.pid)], stderr=null)
+ null.close()
+ _log.warning('killed')
+ self._reset()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
index c3e97be..edef485 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
@@ -52,31 +52,28 @@ class TestPort(base.Port):
def baseline_search_path(self):
return [self.baseline_path()]
- def check_sys_deps(self):
+ def check_build(self, needs_http):
return True
- def diff_image(self, actual_filename, expected_filename,
- diff_filename=None):
+ def compare_text(self, expected_text, actual_text):
return False
- def compare_text(self, actual_text, expected_text):
+ def diff_image(self, expected_filename, actual_filename,
+ diff_filename=None):
return False
- def diff_text(self, actual_text, expected_text,
- actual_filename, expected_filename):
+ def diff_text(self, expected_text, actual_text,
+ expected_filename, actual_filename):
return ''
def name(self):
return self._name
- def num_cores(self):
- return int(os.popen2("sysctl -n hw.ncpu")[1].read())
-
def options(self):
return self._options
def results_directory(self):
- return '/tmp' + self._options.results_directory
+ return '/tmp/' + self._options.results_directory
def setup_test_run(self):
pass
@@ -93,18 +90,12 @@ class TestPort(base.Port):
def start_websocket_server(self):
pass
- def start_helper(self):
- pass
-
def stop_http_server(self):
pass
def stop_websocket_server(self):
pass
- def stop_helper(self):
- pass
-
def test_expectations(self):
return ''
@@ -120,7 +111,7 @@ class TestPort(base.Port):
def version():
return ''
- def wdiff_text(self, actual_filename, expected_filename):
+ def wdiff_text(self, expected_filename, actual_filename):
return ''
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
new file mode 100644
index 0000000..f2f5237
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
@@ -0,0 +1,448 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 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 Google name 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.
+
+"""WebKit implementations of the Port interface."""
+
+import logging
+import os
+import pdb
+import platform
+import re
+import shutil
+import signal
+import subprocess
+import sys
+import time
+import webbrowser
+
+import webkitpy.common.system.ospath as ospath
+import webkitpy.layout_tests.port.base as base
+import webkitpy.layout_tests.port.server_process as server_process
+
+_log = logging.getLogger("webkitpy.layout_tests.port.webkit")
+
+
+class WebKitPort(base.Port):
+ """WebKit implementation of the Port class."""
+
+ def __init__(self, port_name=None, options=None):
+ base.Port.__init__(self, port_name, options)
+ self._cached_build_root = None
+ self._cached_apache_path = None
+
+ # FIXME: disable pixel tests until they are run by default on the
+ # build machines.
+ if options and (not hasattr(options, "pixel_tests") or
+ options.pixel_tests is None):
+ options.pixel_tests = False
+
+ def baseline_path(self):
+ return self._webkit_baseline_path(self._name)
+
+ def baseline_search_path(self):
+ return [self._webkit_baseline_path(self._name)]
+
+ def path_to_test_expectations_file(self):
+ return os.path.join(self._webkit_baseline_path(self._name),
+ 'test_expectations.txt')
+
+ # Only needed by ports which maintain versioned test expectations (like mac-tiger vs. mac-leopard)
+ def version(self):
+ return ''
+
+ def _build_driver(self):
+ return not self._executive.run_command([
+ self.script_path("build-dumprendertree"),
+ self.flag_from_configuration(self._options.configuration),
+ ], return_exit_code=True)
+
+ def _check_driver(self):
+ driver_path = self._path_to_driver()
+ if not os.path.exists(driver_path):
+ _log.error("DumpRenderTree was not found at %s" % driver_path)
+ return False
+ return True
+
+ def check_build(self, needs_http):
+ if self._options.build and not self._build_driver():
+ return False
+ if not self._check_driver():
+ return False
+ if self._options.pixel_tests:
+ if not self.check_image_diff():
+ return False
+ if not self._check_port_build():
+ return False
+ return True
+
+ def _check_port_build(self):
+ # Ports can override this method to do additional checks.
+ return True
+
+ def check_image_diff(self, override_step=None, logging=True):
+ image_diff_path = self._path_to_image_diff()
+ if not os.path.exists(image_diff_path):
+ _log.error("ImageDiff was not found at %s" % image_diff_path)
+ return False
+ return True
+
+ def diff_image(self, expected_filename, actual_filename,
+ diff_filename=None):
+ """Return True if the two files are different. Also write a delta
+ image of the two images into |diff_filename| if it is not None."""
+
+ # Handle the case where the test didn't actually generate an image.
+ actual_length = os.stat(actual_filename).st_size
+ if actual_length == 0:
+ if diff_filename:
+ shutil.copyfile(actual_filename, expected_filename)
+ return True
+
+ sp = self._diff_image_request(expected_filename, actual_filename)
+ return self._diff_image_reply(sp, expected_filename, diff_filename)
+
+ def _diff_image_request(self, expected_filename, actual_filename):
+ # FIXME: either expose the tolerance argument as a command-line
+ # parameter, or make it go away and aways use exact matches.
+ command = [self._path_to_image_diff(), '--tolerance', '0.1']
+ sp = server_process.ServerProcess(self, 'ImageDiff', command)
+
+ actual_length = os.stat(actual_filename).st_size
+ actual_file = open(actual_filename).read()
+ expected_length = os.stat(expected_filename).st_size
+ expected_file = open(expected_filename).read()
+ sp.write('Content-Length: %d\n%sContent-Length: %d\n%s' %
+ (actual_length, actual_file, expected_length, expected_file))
+
+ return sp
+
+ def _diff_image_reply(self, sp, expected_filename, diff_filename):
+ timeout = 2.0
+ deadline = time.time() + timeout
+ output = sp.read_line(timeout)
+ while not sp.timed_out and not sp.crashed and output:
+ if output.startswith('Content-Length'):
+ m = re.match('Content-Length: (\d+)', output)
+ content_length = int(m.group(1))
+ timeout = deadline - time.time()
+ output = sp.read(timeout, content_length)
+ break
+ elif output.startswith('diff'):
+ break
+ else:
+ timeout = deadline - time.time()
+ output = sp.read_line(deadline)
+
+ result = True
+ if output.startswith('diff'):
+ m = re.match('diff: (.+)% (passed|failed)', output)
+ if m.group(2) == 'passed':
+ result = False
+ elif output and diff_filename:
+ open(diff_filename, 'w').write(output) # FIXME: This leaks a file handle.
+ elif sp.timed_out:
+ _log.error("ImageDiff timed out on %s" % expected_filename)
+ elif sp.crashed:
+ _log.error("ImageDiff crashed")
+ sp.stop()
+ return result
+
+ def results_directory(self):
+ # Results are store relative to the built products to make it easy
+ # to have multiple copies of webkit checked out and built.
+ return self._build_path(self._options.results_directory)
+
+ def setup_test_run(self):
+ # This port doesn't require any specific configuration.
+ pass
+
+ def show_results_html_file(self, results_filename):
+ uri = self.filename_to_uri(results_filename)
+ # FIXME: We should open results in the version of WebKit we built.
+ webbrowser.open(uri, new=1)
+
+ def start_driver(self, image_path, options):
+ return WebKitDriver(self, image_path, options)
+
+ def test_base_platform_names(self):
+ # At the moment we don't use test platform names, but we have
+ # to return something.
+ return ('mac', 'win')
+
+ def _tests_for_other_platforms(self):
+ raise NotImplementedError('WebKitPort._tests_for_other_platforms')
+ # The original run-webkit-tests builds up a "whitelist" of tests to
+ # run, and passes that to DumpRenderTree. new-run-webkit-tests assumes
+ # we run *all* tests and test_expectations.txt functions as a
+ # blacklist.
+ # FIXME: This list could be dynamic based on platform name and
+ # pushed into base.Port.
+ return [
+ "platform/chromium",
+ "platform/gtk",
+ "platform/qt",
+ "platform/win",
+ ]
+
+ def _tests_for_disabled_features(self):
+ # FIXME: This should use the feature detection from
+ # webkitperl/features.pm to match run-webkit-tests.
+ # For now we hard-code a list of features known to be disabled on
+ # the Mac platform.
+ disabled_feature_tests = [
+ "fast/xhtmlmp",
+ "http/tests/wml",
+ "mathml",
+ "wml",
+ ]
+ # FIXME: webarchive tests expect to read-write from
+ # -expected.webarchive files instead of .txt files.
+ # This script doesn't know how to do that yet, so pretend they're
+ # just "disabled".
+ webarchive_tests = [
+ "webarchive",
+ "svg/webarchive",
+ "http/tests/webarchive",
+ "svg/custom/image-with-prefix-in-webarchive.svg",
+ ]
+ return disabled_feature_tests + webarchive_tests
+
+ def _tests_from_skipped_file(self, skipped_file):
+ tests_to_skip = []
+ for line in skipped_file.readlines():
+ line = line.strip()
+ if line.startswith('#') or not len(line):
+ continue
+ tests_to_skip.append(line)
+ return tests_to_skip
+
+ def _skipped_file_paths(self):
+ return [os.path.join(self._webkit_baseline_path(self._name),
+ 'Skipped')]
+
+ def _expectations_from_skipped_files(self):
+ tests_to_skip = []
+ for filename in self._skipped_file_paths():
+ if not os.path.exists(filename):
+ _log.warn("Failed to open Skipped file: %s" % filename)
+ continue
+ skipped_file = file(filename)
+ tests_to_skip.extend(self._tests_from_skipped_file(skipped_file))
+ skipped_file.close()
+ return tests_to_skip
+
+ def test_expectations(self):
+ # The WebKit mac port uses a combination of a test_expectations file
+ # and 'Skipped' files.
+ expectations_file = self.path_to_test_expectations_file()
+ expectations = file(expectations_file, "r").read()
+ return expectations + self._skips()
+
+ def _skips(self):
+ # Each Skipped file contains a list of files
+ # or directories to be skipped during the test run. The total list
+ # of tests to skipped is given by the contents of the generic
+ # Skipped file found in platform/X plus a version-specific file
+ # found in platform/X-version. Duplicate entries are allowed.
+ # This routine reads those files and turns contents into the
+ # format expected by test_expectations.
+
+ # Use a set to allow duplicates
+ tests_to_skip = set(self._expectations_from_skipped_files())
+
+ tests_to_skip.update(self._tests_for_other_platforms())
+ tests_to_skip.update(self._tests_for_disabled_features())
+ skip_lines = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" %
+ test_path, tests_to_skip)
+ return "\n".join(skip_lines)
+
+ def test_platform_name(self):
+ return self._name + self.version()
+
+ def test_platform_names(self):
+ return self.test_base_platform_names() + (
+ 'mac-tiger', 'mac-leopard', 'mac-snowleopard')
+
+ def default_configuration(self):
+ # This is a bit of a hack. This state exists in a much nicer form in
+ # perl-land.
+ configuration = ospath.relpath(
+ self._webkit_build_directory(["--configuration"]),
+ self._webkit_build_directory(["--top-level"]))
+ assert(configuration == "Debug" or configuration == "Release")
+ return configuration
+
+ def _webkit_build_directory(self, args):
+ args = [self.script_path("webkit-build-directory")] + args
+ return self._executive.run_command(args).rstrip()
+
+ def _build_path(self, *comps):
+ if not self._cached_build_root:
+ self._cached_build_root = self._webkit_build_directory([
+ "--configuration",
+ self.flag_from_configuration(self._options.configuration),
+ ])
+ return os.path.join(self._cached_build_root, *comps)
+
+ def _path_to_driver(self):
+ return self._build_path('DumpRenderTree')
+
+ def _path_to_helper(self):
+ return None
+
+ def _path_to_image_diff(self):
+ return self._build_path('ImageDiff')
+
+ def _path_to_wdiff(self):
+ # FIXME: This does not exist on a default Mac OS X Leopard install.
+ return 'wdiff'
+
+ def _path_to_apache(self):
+ if not self._cached_apache_path:
+ # The Apache binary path can vary depending on OS and distribution
+ # See http://wiki.apache.org/httpd/DistrosDefaultLayout
+ for path in ["/usr/sbin/httpd", "/usr/sbin/apache2"]:
+ if os.path.exists(path):
+ self._cached_apache_path = path
+ break
+
+ if not self._cached_apache_path:
+ _log.error("Could not find apache. Not installed or unknown path.")
+
+ return self._cached_apache_path
+
+
+class WebKitDriver(base.Driver):
+ """WebKit implementation of the DumpRenderTree interface."""
+
+ def __init__(self, port, image_path, driver_options):
+ self._port = port
+ self._driver_options = driver_options
+ self._image_path = image_path
+
+ command = []
+ # Hook for injecting valgrind or other runtime instrumentation,
+ # used by e.g. tools/valgrind/valgrind_tests.py.
+ wrapper = os.environ.get("BROWSER_WRAPPER", None)
+ if wrapper != None:
+ command += [wrapper]
+ if self._port._options.wrapper:
+ # This split() isn't really what we want -- it incorrectly will
+ # split quoted strings within the wrapper argument -- but in
+ # practice it shouldn't come up and the --help output warns
+ # about it anyway.
+ # FIXME: Use a real shell parser.
+ command += self._options.wrapper.split()
+
+ command += [port._path_to_driver(), '-']
+
+ if image_path:
+ command.append('--pixel-tests')
+ environment = os.environ
+ environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path()
+ self._server_process = server_process.ServerProcess(self._port,
+ "DumpRenderTree", command, environment)
+
+ def poll(self):
+ return self._server_process.poll()
+
+ def restart(self):
+ self._server_process.stop()
+ self._server_process.start()
+ return
+
+ def returncode(self):
+ return self._server_process.returncode()
+
+ # FIXME: This function is huge.
+ def run_test(self, uri, timeoutms, image_hash):
+ if uri.startswith("file:///"):
+ command = uri[7:]
+ else:
+ command = uri
+
+ if image_hash:
+ command += "'" + image_hash
+ command += "\n"
+
+ # pdb.set_trace()
+ self._server_process.write(command)
+
+ have_seen_content_type = False
+ actual_image_hash = None
+ output = ''
+ image = ''
+
+ timeout = int(timeoutms) / 1000.0
+ deadline = time.time() + timeout
+ line = self._server_process.read_line(timeout)
+ while (not self._server_process.timed_out
+ and not self._server_process.crashed
+ and line.rstrip() != "#EOF"):
+ if (line.startswith('Content-Type:') and not
+ have_seen_content_type):
+ have_seen_content_type = True
+ else:
+ output += line
+ line = self._server_process.read_line(timeout)
+ timeout = deadline - time.time()
+
+ # Now read a second block of text for the optional image data
+ remaining_length = -1
+ HASH_HEADER = 'ActualHash: '
+ LENGTH_HEADER = 'Content-Length: '
+ line = self._server_process.read_line(timeout)
+ while (not self._server_process.timed_out
+ and not self._server_process.crashed
+ and line.rstrip() != "#EOF"):
+ if line.startswith(HASH_HEADER):
+ actual_image_hash = line[len(HASH_HEADER):].strip()
+ elif line.startswith('Content-Type:'):
+ pass
+ elif line.startswith(LENGTH_HEADER):
+ timeout = deadline - time.time()
+ content_length = int(line[len(LENGTH_HEADER):])
+ image = self._server_process.read(timeout, content_length)
+ timeout = deadline - time.time()
+ line = self._server_process.read_line(timeout)
+
+ if self._image_path and len(self._image_path):
+ image_file = file(self._image_path, "wb")
+ image_file.write(image)
+ image_file.close()
+ return (self._server_process.crashed,
+ self._server_process.timed_out,
+ actual_image_hash,
+ output,
+ self._server_process.error)
+
+ def stop(self):
+ if self._server_process:
+ self._server_process.stop()
+ self._server_process = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py
index 54c2f6f..a9ba160 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py
@@ -39,8 +39,13 @@ import tempfile
import time
import urllib
+import factory
import http_server
+from webkitpy.common.system.executive import Executive
+
+_log = logging.getLogger("webkitpy.layout_tests.port.websocket_server")
+
_WS_LOG_PREFIX = 'pywebsocket.ws.log-'
_WSS_LOG_PREFIX = 'pywebsocket.wss.log-'
@@ -59,6 +64,7 @@ def url_is_alive(url):
Return:
True if the url is alive.
"""
+ sleep_time = 0.5
wait_time = 5
while wait_time > 0:
try:
@@ -67,9 +73,9 @@ def url_is_alive(url):
return True
except IOError:
pass
- wait_time -= 1
- # Wait a second and try again.
- time.sleep(1)
+ # Wait for sleep_time before trying again.
+ wait_time -= sleep_time
+ time.sleep(sleep_time)
return False
@@ -86,7 +92,7 @@ class PyWebSocket(http_server.Lighttpd):
def __init__(self, port_obj, output_dir, port=_DEFAULT_WS_PORT,
root=None, use_tls=False,
- register_cygwin=None,
+ register_cygwin=True,
pidfile=None):
"""Args:
output_dir: the absolute path to the layout test result directory
@@ -126,7 +132,7 @@ class PyWebSocket(http_server.Lighttpd):
def start(self):
if not self._web_socket_tests:
- logging.info('No need to start %s server.' % self._server_name)
+ _log.info('No need to start %s server.' % self._server_name)
return
if self.is_running():
raise PyWebSocketNotStarted('%s is already running.' %
@@ -150,27 +156,27 @@ class PyWebSocket(http_server.Lighttpd):
python_interp = sys.executable
pywebsocket_base = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(
- os.path.dirname(os.path.dirname(
- os.path.abspath(__file__)))))), 'pywebsocket')
+ os.path.abspath(__file__)))), 'thirdparty', 'pywebsocket')
pywebsocket_script = os.path.join(pywebsocket_base, 'mod_pywebsocket',
'standalone.py')
start_cmd = [
python_interp, pywebsocket_script,
- '-p', str(self._port),
- '-d', self._layout_tests,
- '-s', self._web_socket_tests,
- '-x', '/websocket/tests/cookies',
- '-l', error_log,
+ '--server-host', '127.0.0.1',
+ '--port', str(self._port),
+ '--document-root', self._layout_tests,
+ '--scan-dir', self._web_socket_tests,
+ '--cgi-paths', '/websocket/tests',
+ '--log-file', error_log,
]
handler_map_file = os.path.join(self._web_socket_tests,
'handler_map.txt')
if os.path.exists(handler_map_file):
- logging.debug('Using handler_map_file: %s' % handler_map_file)
- start_cmd.append('-m')
+ _log.debug('Using handler_map_file: %s' % handler_map_file)
+ start_cmd.append('--websock-handlers-map-file')
start_cmd.append(handler_map_file)
else:
- logging.warning('No handler_map_file found')
+ _log.warning('No handler_map_file found')
if self._use_tls:
start_cmd.extend(['-t', '-k', self._private_key,
@@ -183,6 +189,8 @@ class PyWebSocket(http_server.Lighttpd):
self._port_obj.path_from_chromium_base('third_party',
'cygwin', 'bin'),
env['PATH'])
+ env['CYGWIN_PATH'] = self._port_obj.path_from_chromium_base(
+ 'third_party', 'cygwin', 'bin')
if sys.platform == 'win32' and self._register_cygwin:
setup_mount = self._port_obj.path_from_chromium_base(
@@ -192,16 +200,16 @@ class PyWebSocket(http_server.Lighttpd):
env['PYTHONPATH'] = (pywebsocket_base + os.path.pathsep +
env.get('PYTHONPATH', ''))
- logging.debug('Starting %s server on %d.' % (
- self._server_name, self._port))
- logging.debug('cmdline: %s' % ' '.join(start_cmd))
- self._process = subprocess.Popen(start_cmd, stdout=self._wsout,
+ _log.debug('Starting %s server on %d.' % (
+ self._server_name, self._port))
+ _log.debug('cmdline: %s' % ' '.join(start_cmd))
+ # FIXME: We should direct this call through Executive for testing.
+ self._process = subprocess.Popen(start_cmd,
+ stdin=open(os.devnull, 'r'),
+ stdout=self._wsout,
stderr=subprocess.STDOUT,
env=env)
- # Wait a bit before checking the liveness of the server.
- time.sleep(0.5)
-
if self._use_tls:
url = 'https'
else:
@@ -211,7 +219,7 @@ class PyWebSocket(http_server.Lighttpd):
fp = open(output_log)
try:
for line in fp:
- logging.error(line)
+ _log.error(line)
finally:
fp.close()
raise PyWebSocketNotStarted(
@@ -231,6 +239,7 @@ class PyWebSocket(http_server.Lighttpd):
if not force and not self.is_running():
return
+ pid = None
if self._process:
pid = self._process.pid
elif self._pidfile:
@@ -242,8 +251,9 @@ class PyWebSocket(http_server.Lighttpd):
raise PyWebSocketNotFound(
'Failed to find %s server pid.' % self._server_name)
- logging.debug('Shutting down %s server %d.' % (self._server_name, pid))
- self._port_obj._kill_process(pid)
+ _log.debug('Shutting down %s server %d.' % (self._server_name, pid))
+ # FIXME: We should use a non-static Executive for easier testing.
+ Executive().kill_process(pid)
if self._process:
self._process.wait()
@@ -252,53 +262,3 @@ class PyWebSocket(http_server.Lighttpd):
if self._wsout:
self._wsout.close()
self._wsout = None
-
-
-if '__main__' == __name__:
- # Provide some command line params for starting the PyWebSocket server
- # manually.
- option_parser = optparse.OptionParser()
- option_parser.add_option('--server', type='choice',
- choices=['start', 'stop'], default='start',
- help='Server action (start|stop)')
- option_parser.add_option('-p', '--port', dest='port',
- default=None, help='Port to listen on')
- option_parser.add_option('-r', '--root',
- help='Absolute path to DocumentRoot '
- '(overrides layout test roots)')
- option_parser.add_option('-t', '--tls', dest='use_tls',
- action='store_true',
- default=False, help='use TLS (wss://)')
- option_parser.add_option('-k', '--private_key', dest='private_key',
- default='', help='TLS private key file.')
- option_parser.add_option('-c', '--certificate', dest='certificate',
- default='', help='TLS certificate file.')
- option_parser.add_option('--register_cygwin', action="store_true",
- dest="register_cygwin",
- help='Register Cygwin paths (on Win try bots)')
- option_parser.add_option('--pidfile', help='path to pid file.')
- options, args = option_parser.parse_args()
-
- if not options.port:
- if options.use_tls:
- options.port = _DEFAULT_WSS_PORT
- else:
- options.port = _DEFAULT_WS_PORT
-
- kwds = {'port': options.port, 'use_tls': options.use_tls}
- if options.root:
- kwds['root'] = options.root
- if options.private_key:
- kwds['private_key'] = options.private_key
- if options.certificate:
- kwds['certificate'] = options.certificate
- kwds['register_cygwin'] = options.register_cygwin
- if options.pidfile:
- kwds['pidfile'] = options.pidfile
-
- pywebsocket = PyWebSocket(tempfile.gettempdir(), **kwds)
-
- if 'start' == options.server:
- pywebsocket.start()
- else:
- pywebsocket.stop(force=True)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py
new file mode 100644
index 0000000..2bf692b
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2010 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 Google name 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.
+
+"""WebKit Win implementation of the Port interface."""
+
+import logging
+import os
+import subprocess
+
+from webkitpy.layout_tests.port.webkit import WebKitPort
+
+_log = logging.getLogger("webkitpy.layout_tests.port.win")
+
+
+class WinPort(WebKitPort):
+ """WebKit Win implementation of the Port class."""
+
+ def __init__(self, port_name=None, options=None):
+ if port_name is None:
+ port_name = 'win'
+ WebKitPort.__init__(self, port_name, options)
+
+ def _tests_for_other_platforms(self):
+ # FIXME: This list could be dynamic based on platform name and
+ # pushed into base.Port.
+ # This really need to be automated.
+ return [
+ "platform/chromium",
+ "platform/gtk",
+ "platform/qt",
+ "platform/mac",
+ ]
+
+ def _path_to_apache_config_file(self):
+ return os.path.join(self.layout_tests_dir(), 'http', 'conf',
+ 'cygwin-httpd.conf')
+
+ def _shut_down_http_server(self, server_pid):
+ """Shut down the httpd web server. Blocks until it's fully
+ shut down.
+
+ Args:
+ server_pid: The process ID of the running server.
+ """
+ # Looks like we ignore server_pid.
+ # Copy/pasted from chromium-win.
+ subprocess.Popen(('taskkill.exe', '/f', '/im', 'httpd.exe'),
+ stdin=open(os.devnull, 'r'),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).wait()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py
index 4604a1a..b972154 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py
@@ -41,6 +41,7 @@ The script does the following for each platform specified:
At the end, the script generates a html that compares old and new baselines.
"""
+import copy
import logging
import optparse
import os
@@ -59,6 +60,9 @@ from layout_package import test_expectations
from test_types import image_diff
from test_types import text_diff
+_log = logging.getLogger("webkitpy.layout_tests."
+ "rebaseline_chromium_webkit_tests")
+
# Repository type constants.
REPO_SVN, REPO_UNKNOWN = range(2)
@@ -137,11 +141,11 @@ def log_dashed_string(text, platform, logging_level=logging.INFO):
msg = '%s %s %s' % (dashes, msg, dashes)
if logging_level == logging.ERROR:
- logging.error(msg)
+ _log.error(msg)
elif logging_level == logging.WARNING:
- logging.warn(msg)
+ _log.warn(msg)
else:
- logging.info(msg)
+ _log.info(msg)
def setup_html_directory(html_directory):
@@ -163,11 +167,11 @@ def setup_html_directory(html_directory):
os.mkdir(html_directory)
html_directory = os.path.join(html_directory, 'rebaseline_html')
- logging.info('Html directory: "%s"', html_directory)
+ _log.info('Html directory: "%s"', html_directory)
if os.path.exists(html_directory):
shutil.rmtree(html_directory, True)
- logging.info('Deleted file at html directory: "%s"', html_directory)
+ _log.info('Deleted file at html directory: "%s"', html_directory)
if not os.path.exists(html_directory):
os.mkdir(html_directory)
@@ -191,7 +195,7 @@ def get_result_file_fullpath(html_directory, baseline_filename, platform,
base, ext = os.path.splitext(baseline_filename)
result_filename = '%s-%s-%s%s' % (base, platform, result_type, ext)
fullpath = os.path.join(html_directory, result_filename)
- logging.debug(' Result file full path: "%s".', fullpath)
+ _log.debug(' Result file full path: "%s".', fullpath)
return fullpath
@@ -200,12 +204,21 @@ class Rebaseliner(object):
REVISION_REGEX = r'<a href=\"(\d+)/\">'
- def __init__(self, port, platform, options):
- self._file_dir = port.path_from_chromium_base('webkit', 'tools',
- 'layout_tests')
- self._port = port
+ def __init__(self, running_port, target_port, platform, options):
+ """
+ Args:
+ running_port: the Port the script is running on.
+ target_port: the Port the script uses to find port-specific
+ configuration information like the test_expectations.txt
+ file location and the list of test platforms.
+ platform: the test platform to rebaseline
+ options: the command-line options object."""
self._platform = platform
self._options = options
+ self._port = running_port
+ self._target_port = target_port
+ self._rebaseline_port = port.get(
+ self._target_port.test_platform_name_to_name(platform), options)
self._rebaselining_tests = []
self._rebaselined_tests = []
@@ -213,9 +226,9 @@ class Rebaseliner(object):
# -. compile list of tests that need rebaselining.
# -. update the tests in test_expectations file after rebaseline
# is done.
- expectations_str = self._port.test_expectations()
+ expectations_str = self._rebaseline_port.test_expectations()
self._test_expectations = \
- test_expectations.TestExpectations(self._port,
+ test_expectations.TestExpectations(self._rebaseline_port,
None,
expectations_str,
self._platform,
@@ -233,9 +246,9 @@ class Rebaseliner(object):
log_dashed_string('Downloading archive', self._platform)
archive_file = self._download_buildbot_archive()
- logging.info('')
+ _log.info('')
if not archive_file:
- logging.error('No archive found.')
+ _log.error('No archive found.')
return False
log_dashed_string('Extracting and adding new baselines',
@@ -246,19 +259,19 @@ class Rebaseliner(object):
log_dashed_string('Updating rebaselined tests in file',
self._platform)
self._update_rebaselined_tests_in_file(backup)
- logging.info('')
+ _log.info('')
if len(self._rebaselining_tests) != len(self._rebaselined_tests):
- logging.warning('NOT ALL TESTS THAT NEED REBASELINING HAVE BEEN '
- 'REBASELINED.')
- logging.warning(' Total tests needing rebaselining: %d',
- len(self._rebaselining_tests))
- logging.warning(' Total tests rebaselined: %d',
- len(self._rebaselined_tests))
+ _log.warning('NOT ALL TESTS THAT NEED REBASELINING HAVE BEEN '
+ 'REBASELINED.')
+ _log.warning(' Total tests needing rebaselining: %d',
+ len(self._rebaselining_tests))
+ _log.warning(' Total tests rebaselined: %d',
+ len(self._rebaselined_tests))
return False
- logging.warning('All tests needing rebaselining were successfully '
- 'rebaselined.')
+ _log.warning('All tests needing rebaselining were successfully '
+ 'rebaselined.')
return True
@@ -285,16 +298,16 @@ class Rebaseliner(object):
self._rebaselining_tests = \
self._test_expectations.get_rebaselining_failures()
if not self._rebaselining_tests:
- logging.warn('No tests found that need rebaselining.')
+ _log.warn('No tests found that need rebaselining.')
return None
- logging.info('Total number of tests needing rebaselining '
- 'for "%s": "%d"', self._platform,
- len(self._rebaselining_tests))
+ _log.info('Total number of tests needing rebaselining '
+ 'for "%s": "%d"', self._platform,
+ len(self._rebaselining_tests))
test_no = 1
for test in self._rebaselining_tests:
- logging.info(' %d: %s', test_no, test)
+ _log.info(' %d: %s', test_no, test)
test_no += 1
return self._rebaselining_tests
@@ -310,7 +323,7 @@ class Rebaseliner(object):
None on failure.
"""
- logging.debug('Url to retrieve revision: "%s"', url)
+ _log.debug('Url to retrieve revision: "%s"', url)
f = urllib.urlopen(url)
content = f.read()
@@ -318,11 +331,11 @@ class Rebaseliner(object):
revisions = re.findall(self.REVISION_REGEX, content)
if not revisions:
- logging.error('Failed to find revision, content: "%s"', content)
+ _log.error('Failed to find revision, content: "%s"', content)
return None
revisions.sort(key=int)
- logging.info('Latest revision: "%s"', revisions[len(revisions) - 1])
+ _log.info('Latest revision: "%s"', revisions[len(revisions) - 1])
return revisions[len(revisions) - 1]
def _get_archive_dir_name(self, platform, webkit_canary):
@@ -339,8 +352,8 @@ class Rebaseliner(object):
if platform in ARCHIVE_DIR_NAME_DICT:
return ARCHIVE_DIR_NAME_DICT[platform]
else:
- logging.error('Cannot find platform key %s in archive '
- 'directory name dictionary', platform)
+ _log.error('Cannot find platform key %s in archive '
+ 'directory name dictionary', platform)
return None
def _get_archive_url(self):
@@ -356,7 +369,7 @@ class Rebaseliner(object):
if not dir_name:
return None
- logging.debug('Buildbot platform dir name: "%s"', dir_name)
+ _log.debug('Buildbot platform dir name: "%s"', dir_name)
url_base = '%s/%s/' % (self._options.archive_url, dir_name)
latest_revision = self._get_latest_revision(url_base)
@@ -364,7 +377,7 @@ class Rebaseliner(object):
return None
archive_url = ('%s%s/layout-test-results.zip' % (url_base,
latest_revision))
- logging.info('Archive url: "%s"', archive_url)
+ _log.info('Archive url: "%s"', archive_url)
return archive_url
def _download_buildbot_archive(self):
@@ -380,7 +393,7 @@ class Rebaseliner(object):
return None
fn = urllib.urlretrieve(url)[0]
- logging.info('Archive downloaded and saved to file: "%s"', fn)
+ _log.info('Archive downloaded and saved to file: "%s"', fn)
return fn
def _extract_and_add_new_baselines(self, archive_file):
@@ -397,17 +410,18 @@ class Rebaseliner(object):
zip_file = zipfile.ZipFile(archive_file, 'r')
zip_namelist = zip_file.namelist()
- logging.debug('zip file namelist:')
+ _log.debug('zip file namelist:')
for name in zip_namelist:
- logging.debug(' ' + name)
+ _log.debug(' ' + name)
- platform = self._port.name()
- logging.debug('Platform dir: "%s"', platform)
+ platform = self._rebaseline_port.test_platform_name_to_name(
+ self._platform)
+ _log.debug('Platform dir: "%s"', platform)
test_no = 1
self._rebaselined_tests = []
for test in self._rebaselining_tests:
- logging.info('Test %d: %s', test_no, test)
+ _log.info('Test %d: %s', test_no, test)
found = False
svn_error = False
@@ -415,14 +429,14 @@ class Rebaseliner(object):
for suffix in BASELINE_SUFFIXES:
archive_test_name = ('layout-test-results/%s-actual%s' %
(test_basename, suffix))
- logging.debug(' Archive test file name: "%s"',
- archive_test_name)
+ _log.debug(' Archive test file name: "%s"',
+ archive_test_name)
if not archive_test_name in zip_namelist:
- logging.info(' %s file not in archive.', suffix)
+ _log.info(' %s file not in archive.', suffix)
continue
found = True
- logging.info(' %s file found in archive.', suffix)
+ _log.info(' %s file found in archive.', suffix)
# Extract new baseline from archive and save it to a temp file.
data = zip_file.read(archive_test_name)
@@ -433,11 +447,10 @@ class Rebaseliner(object):
expected_filename = '%s-expected%s' % (test_basename, suffix)
expected_fullpath = os.path.join(
- self._port._chromium_baseline_path(platform),
- expected_filename)
+ self._rebaseline_port.baseline_path(), expected_filename)
expected_fullpath = os.path.normpath(expected_fullpath)
- logging.debug(' Expected file full path: "%s"',
- expected_fullpath)
+ _log.debug(' Expected file full path: "%s"',
+ expected_fullpath)
# TODO(victorw): for now, the rebaselining tool checks whether
# or not THIS baseline is duplicate and should be skipped.
@@ -466,12 +479,12 @@ class Rebaseliner(object):
self._create_html_baseline_files(expected_fullpath)
if not found:
- logging.warn(' No new baselines found in archive.')
+ _log.warn(' No new baselines found in archive.')
else:
if svn_error:
- logging.warn(' Failed to add baselines to SVN.')
+ _log.warn(' Failed to add baselines to SVN.')
else:
- logging.info(' Rebaseline succeeded.')
+ _log.info(' Rebaseline succeeded.')
self._rebaselined_tests.append(test)
test_no += 1
@@ -499,9 +512,10 @@ class Rebaseliner(object):
True if the baseline is unnecessary.
False otherwise.
"""
- test_filepath = os.path.join(self._port.layout_tests_dir(), test)
- all_baselines = self._port.expected_baselines(test_filepath,
- suffix, True)
+ test_filepath = os.path.join(self._target_port.layout_tests_dir(),
+ test)
+ all_baselines = self._rebaseline_port.expected_baselines(
+ test_filepath, suffix, True)
for (fallback_dir, fallback_file) in all_baselines:
if fallback_dir and fallback_file:
fallback_fullpath = os.path.normpath(
@@ -509,8 +523,8 @@ class Rebaseliner(object):
if fallback_fullpath.lower() != baseline_path.lower():
if not self._diff_baselines(new_baseline,
fallback_fullpath):
- logging.info(' Found same baseline at %s',
- fallback_fullpath)
+ _log.info(' Found same baseline at %s',
+ fallback_fullpath)
return True
else:
return False
@@ -531,15 +545,15 @@ class Rebaseliner(object):
ext1 = os.path.splitext(file1)[1].upper()
ext2 = os.path.splitext(file2)[1].upper()
if ext1 != ext2:
- logging.warn('Files to compare have different ext. '
- 'File1: %s; File2: %s', file1, file2)
+ _log.warn('Files to compare have different ext. '
+ 'File1: %s; File2: %s', file1, file2)
return True
if ext1 == '.PNG':
- return image_diff.ImageDiff(self._port, self._platform,
- '').diff_files(self._port, file1, file2)
+ return image_diff.ImageDiff(self._port,
+ '').diff_files(self._port, file1, file2)
else:
- return text_diff.TestTextDiff(self._port, self._platform,
+ return text_diff.TestTextDiff(self._port,
'').diff_files(self._port, file1, file2)
def _delete_baseline(self, filename):
@@ -575,20 +589,20 @@ class Rebaseliner(object):
new_expectations = (
self._test_expectations.remove_platform_from_expectations(
self._rebaselined_tests, self._platform))
- path = self._port.path_to_test_expectations_file()
+ path = self._target_port.path_to_test_expectations_file()
if backup:
date_suffix = time.strftime('%Y%m%d%H%M%S',
time.localtime(time.time()))
backup_file = ('%s.orig.%s' % (path, date_suffix))
if os.path.exists(backup_file):
os.remove(backup_file)
- logging.info('Saving original file to "%s"', backup_file)
+ _log.info('Saving original file to "%s"', backup_file)
os.rename(path, backup_file)
f = open(path, "w")
f.write(new_expectations)
f.close()
else:
- logging.info('No test was rebaselined so nothing to remove.')
+ _log.info('No test was rebaselined so nothing to remove.')
def _svn_add(self, filename):
"""Add the file to SVN repository.
@@ -607,7 +621,7 @@ class Rebaseliner(object):
parent_dir, basename = os.path.split(filename)
if self._repo_type != REPO_SVN or parent_dir == filename:
- logging.info("No svn checkout found, skip svn add.")
+ _log.info("No svn checkout found, skip svn add.")
return True
original_dir = os.getcwd()
@@ -616,12 +630,12 @@ class Rebaseliner(object):
os.chdir(original_dir)
output = status_output.upper()
if output.startswith('A') or output.startswith('M'):
- logging.info(' File already added to SVN: "%s"', filename)
+ _log.info(' File already added to SVN: "%s"', filename)
return True
if output.find('IS NOT A WORKING COPY') >= 0:
- logging.info(' File is not a working copy, add its parent: "%s"',
- parent_dir)
+ _log.info(' File is not a working copy, add its parent: "%s"',
+ parent_dir)
return self._svn_add(parent_dir)
os.chdir(parent_dir)
@@ -629,19 +643,19 @@ class Rebaseliner(object):
os.chdir(original_dir)
output = add_output.upper().rstrip()
if output.startswith('A') and output.find(basename.upper()) >= 0:
- logging.info(' Added new file: "%s"', filename)
+ _log.info(' Added new file: "%s"', filename)
self._svn_prop_set(filename)
return True
if (not status_output) and (add_output.upper().find(
'ALREADY UNDER VERSION CONTROL') >= 0):
- logging.info(' File already under SVN and has no change: "%s"',
- filename)
+ _log.info(' File already under SVN and has no change: "%s"',
+ filename)
return True
- logging.warn(' Failed to add file to SVN: "%s"', filename)
- logging.warn(' Svn status output: "%s"', status_output)
- logging.warn(' Svn add output: "%s"', add_output)
+ _log.warn(' Failed to add file to SVN: "%s"', filename)
+ _log.warn(' Svn status output: "%s"', status_output)
+ _log.warn(' Svn add output: "%s"', add_output)
return False
def _svn_prop_set(self, filename):
@@ -667,7 +681,7 @@ class Rebaseliner(object):
else:
cmd = ['svn', 'pset', 'svn:eol-style', 'LF', basename]
- logging.debug(' Set svn prop: %s', ' '.join(cmd))
+ _log.debug(' Set svn prop: %s', ' '.join(cmd))
run_shell(cmd, False)
os.chdir(original_dir)
@@ -689,14 +703,14 @@ class Rebaseliner(object):
baseline_filename, self._platform,
'new')
shutil.copyfile(baseline_fullpath, new_file)
- logging.info(' Html: copied new baseline file from "%s" to "%s".',
- baseline_fullpath, new_file)
+ _log.info(' Html: copied new baseline file from "%s" to "%s".',
+ baseline_fullpath, new_file)
# Get the old baseline from SVN and save to the html directory.
output = run_shell(['svn', 'cat', '-r', 'BASE', baseline_fullpath])
if (not output) or (output.upper().rstrip().endswith(
'NO SUCH FILE OR DIRECTORY')):
- logging.info(' No base file: "%s"', baseline_fullpath)
+ _log.info(' No base file: "%s"', baseline_fullpath)
return
base_file = get_result_file_fullpath(self._options.html_directory,
baseline_filename, self._platform,
@@ -704,8 +718,8 @@ class Rebaseliner(object):
f = open(base_file, 'wb')
f.write(output)
f.close()
- logging.info(' Html: created old baseline file: "%s".',
- base_file)
+ _log.info(' Html: created old baseline file: "%s".',
+ base_file)
# Get the diff between old and new baselines and save to the html dir.
if baseline_filename.upper().endswith('.TXT'):
@@ -721,7 +735,7 @@ class Rebaseliner(object):
else:
parent_dir = sys.path[0] # tempdir is not secure.
bogus_dir = os.path.join(parent_dir, "temp_svn_config")
- logging.debug(' Html: temp config dir: "%s".', bogus_dir)
+ _log.debug(' Html: temp config dir: "%s".', bogus_dir)
if not os.path.exists(bogus_dir):
os.mkdir(bogus_dir)
delete_bogus_dir = True
@@ -737,13 +751,13 @@ class Rebaseliner(object):
f = open(diff_file, 'wb')
f.write(output)
f.close()
- logging.info(' Html: created baseline diff file: "%s".',
- diff_file)
+ _log.info(' Html: created baseline diff file: "%s".',
+ diff_file)
if delete_bogus_dir:
shutil.rmtree(bogus_dir, True)
- logging.debug(' Html: removed temp config dir: "%s".',
- bogus_dir)
+ _log.debug(' Html: removed temp config dir: "%s".',
+ bogus_dir)
class HtmlGenerator(object):
@@ -792,9 +806,9 @@ class HtmlGenerator(object):
'<img style="width: 200" src="%(uri)s" /></a></td>')
HTML_TR = '<tr>%s</tr>'
- def __init__(self, port, options, platforms, rebaselining_tests):
+ def __init__(self, target_port, options, platforms, rebaselining_tests):
self._html_directory = options.html_directory
- self._port = port
+ self._target_port = target_port
self._platforms = platforms
self._rebaselining_tests = rebaselining_tests
self._html_file = os.path.join(options.html_directory,
@@ -803,7 +817,7 @@ class HtmlGenerator(object):
def generate_html(self):
"""Generate html file for rebaselining result comparison."""
- logging.info('Generating html file')
+ _log.info('Generating html file')
html_body = ''
if not self._rebaselining_tests:
@@ -814,29 +828,29 @@ class HtmlGenerator(object):
test_no = 1
for test in tests:
- logging.info('Test %d: %s', test_no, test)
+ _log.info('Test %d: %s', test_no, test)
html_body += self._generate_html_for_one_test(test)
html = self.HTML_REBASELINE % ({'time': time.asctime(),
'body': html_body})
- logging.debug(html)
+ _log.debug(html)
f = open(self._html_file, 'w')
f.write(html)
f.close()
- logging.info('Baseline comparison html generated at "%s"',
- self._html_file)
+ _log.info('Baseline comparison html generated at "%s"',
+ self._html_file)
def show_html(self):
"""Launch the rebaselining html in brwoser."""
- logging.info('Launching html: "%s"', self._html_file)
+ _log.info('Launching html: "%s"', self._html_file)
- html_uri = self._port.filename_to_uri(self._html_file)
+ html_uri = self._target_port.filename_to_uri(self._html_file)
webbrowser.open(html_uri, 1)
- logging.info('Html launched.')
+ _log.info('Html launched.')
def _generate_baseline_links(self, test_basename, suffix, platform):
"""Generate links for baseline results (old, new and diff).
@@ -851,18 +865,18 @@ class HtmlGenerator(object):
"""
baseline_filename = '%s-expected%s' % (test_basename, suffix)
- logging.debug(' baseline filename: "%s"', baseline_filename)
+ _log.debug(' baseline filename: "%s"', baseline_filename)
new_file = get_result_file_fullpath(self._html_directory,
baseline_filename, platform, 'new')
- logging.info(' New baseline file: "%s"', new_file)
+ _log.info(' New baseline file: "%s"', new_file)
if not os.path.exists(new_file):
- logging.info(' No new baseline file: "%s"', new_file)
+ _log.info(' No new baseline file: "%s"', new_file)
return ''
old_file = get_result_file_fullpath(self._html_directory,
baseline_filename, platform, 'old')
- logging.info(' Old baseline file: "%s"', old_file)
+ _log.info(' Old baseline file: "%s"', old_file)
if suffix == '.png':
html_td_link = self.HTML_TD_LINK_IMG
else:
@@ -871,24 +885,25 @@ class HtmlGenerator(object):
links = ''
if os.path.exists(old_file):
links += html_td_link % {
- 'uri': self._port.filename_to_uri(old_file),
+ 'uri': self._target_port.filename_to_uri(old_file),
'name': baseline_filename}
else:
- logging.info(' No old baseline file: "%s"', old_file)
+ _log.info(' No old baseline file: "%s"', old_file)
links += self.HTML_TD_NOLINK % ''
- links += html_td_link % {'uri': self._port.filename_to_uri(new_file),
+ links += html_td_link % {'uri': self._target_port.filename_to_uri(
+ new_file),
'name': baseline_filename}
diff_file = get_result_file_fullpath(self._html_directory,
baseline_filename, platform,
'diff')
- logging.info(' Baseline diff file: "%s"', diff_file)
+ _log.info(' Baseline diff file: "%s"', diff_file)
if os.path.exists(diff_file):
- links += html_td_link % {'uri': self._port.filename_to_uri(
+ links += html_td_link % {'uri': self._target_port.filename_to_uri(
diff_file), 'name': 'Diff'}
else:
- logging.info(' No baseline diff file: "%s"', diff_file)
+ _log.info(' No baseline diff file: "%s"', diff_file)
links += self.HTML_TD_NOLINK % ''
return links
@@ -904,13 +919,13 @@ class HtmlGenerator(object):
"""
test_basename = os.path.basename(os.path.splitext(test)[0])
- logging.info(' basename: "%s"', test_basename)
+ _log.info(' basename: "%s"', test_basename)
rows = []
for suffix in BASELINE_SUFFIXES:
if suffix == '.checksum':
continue
- logging.info(' Checking %s files', suffix)
+ _log.info(' Checking %s files', suffix)
for platform in self._platforms:
links = self._generate_baseline_links(test_basename, suffix,
platform)
@@ -919,17 +934,18 @@ class HtmlGenerator(object):
suffix)
row += self.HTML_TD_NOLINK % platform
row += links
- logging.debug(' html row: %s', row)
+ _log.debug(' html row: %s', row)
rows.append(self.HTML_TR % row)
if rows:
- test_path = os.path.join(self._port.layout_tests_dir(), test)
- html = self.HTML_TR_TEST % (self._port.filename_to_uri(test_path),
- test)
+ test_path = os.path.join(self._target_port.layout_tests_dir(),
+ test)
+ html = self.HTML_TR_TEST % (
+ self._target_port.filename_to_uri(test_path), test)
html += self.HTML_TEST_DETAIL % ' '.join(rows)
- logging.debug(' html for test: %s', html)
+ _log.debug(' html for test: %s', html)
return self.HTML_TABLE_TEST % html
return ''
@@ -982,8 +998,23 @@ def main():
help=('The directory that stores the results for'
' rebaselining comparison.'))
+ option_parser.add_option('', '--target-platform',
+ default='chromium',
+ help=('The target platform to rebaseline '
+ '("mac", "chromium", "qt", etc.). Defaults '
+ 'to "chromium".'))
options = option_parser.parse_args()[0]
- port_obj = port.get(None, options)
+
+ # We need to create three different Port objects over the life of this
+ # script. |target_port_obj| is used to determine configuration information:
+ # location of the expectations file, names of ports to rebaseline, etc.
+ # |port_obj| is used for runtime functionality like actually diffing
+ # Then we create a rebaselining port to actual find and manage the
+ # baselines.
+ target_options = copy.copy(options)
+ if options.target_platform == 'chromium':
+ target_options.chromium = True
+ target_port_obj = port.get(None, target_options)
# Set up our logging format.
log_level = logging.INFO
@@ -994,15 +1025,27 @@ def main():
'%(levelname)s %(message)s'),
datefmt='%y%m%d %H:%M:%S')
+ # options.configuration is used by port to locate image_diff binary.
+ # Check the imgage_diff release binary, if it does not exist,
+ # fallback to debug.
+ options.configuration = "Release"
+ port_obj = port.get(None, options)
+ if not port_obj.check_image_diff(override_step=None, logging=False):
+ _log.debug('No release version image diff binary found.')
+ options.configuration = "Debug"
+ port_obj = port.get(None, options)
+ else:
+ _log.debug('Found release version image diff binary.')
+
# Verify 'platforms' option is valid
if not options.platforms:
- logging.error('Invalid "platforms" option. --platforms must be '
- 'specified in order to rebaseline.')
+ _log.error('Invalid "platforms" option. --platforms must be '
+ 'specified in order to rebaseline.')
sys.exit(1)
platforms = [p.strip().lower() for p in options.platforms.split(',')]
for platform in platforms:
if not platform in REBASELINE_PLATFORM_ORDER:
- logging.error('Invalid platform: "%s"' % (platform))
+ _log.error('Invalid platform: "%s"' % (platform))
sys.exit(1)
# Adjust the platform order so rebaseline tool is running at the order of
@@ -1019,9 +1062,9 @@ def main():
rebaselining_tests = set()
backup = options.backup
for platform in rebaseline_platforms:
- rebaseliner = Rebaseliner(port_obj, platform, options)
+ rebaseliner = Rebaseliner(port_obj, target_port_obj, platform, options)
- logging.info('')
+ _log.info('')
log_dashed_string('Rebaseline started', platform)
if rebaseliner.run(backup):
# Only need to backup one original copy of test expectation file.
@@ -1032,9 +1075,9 @@ def main():
rebaselining_tests |= set(rebaseliner.get_rebaselining_tests())
- logging.info('')
+ _log.info('')
log_dashed_string('Rebaselining result comparison started', None)
- html_generator = HtmlGenerator(port_obj,
+ html_generator = HtmlGenerator(target_port_obj,
options,
rebaseline_platforms,
rebaselining_tests)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index f0b68ee..73195b3 100755
--- a/WebKitTools/Scripts/webkitpy/layout_tests/run_chromium_webkit_tests.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -27,7 +27,7 @@
# (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 layout tests using the test_shell.
+"""Run layout tests.
This is a port of the existing webkit test script run-webkit-tests.
@@ -44,12 +44,16 @@ directory. Entire lines starting with '//' (comments) will be ignored.
For details of the files' contents and purposes, see test_lists/README.
"""
+from __future__ import with_statement
+
+import codecs
import errno
import glob
import logging
import math
import optparse
import os
+import platform
import Queue
import random
import re
@@ -58,27 +62,50 @@ import sys
import time
import traceback
-import simplejson
-
from layout_package import test_expectations
from layout_package import json_layout_results_generator
from layout_package import metered_stream
from layout_package import test_failures
-from layout_package import test_shell_thread
+from layout_package import dump_render_tree_thread
from layout_package import test_files
from test_types import fuzzy_image_diff
from test_types import image_diff
from test_types import test_type_base
from test_types import text_diff
+from webkitpy.common.system.executive import Executive
+from webkitpy.thirdparty import simplejson
+
import port
+_log = logging.getLogger("webkitpy.layout_tests.run_webkit_tests")
+
+# dummy value used for command-line explicitness to disable defaults
+LOG_NOTHING = 'nothing'
+
+# Display the one-line progress bar (% completed) while testing
+LOG_PROGRESS = 'progress'
+
# Indicates that we want detailed progress updates in the output (prints
# directory-by-directory feedback).
LOG_DETAILED_PROGRESS = 'detailed-progress'
+# Log the one-line summary at the end of the run
+LOG_SUMMARY = 'summary'
+
+# "Trace" the test - log the expected result, the actual result, and the
+# baselines used
+LOG_TRACE = 'trace'
+
# Log any unexpected results while running (instead of just at the end).
LOG_UNEXPECTED = 'unexpected'
+LOG_UNEXPECTED_RESULTS = 'unexpected-results'
+
+LOG_VALUES = ",".join(("actual", "config", LOG_DETAILED_PROGRESS, "expected",
+ LOG_NOTHING, LOG_PROGRESS, LOG_SUMMARY, "timing",
+ LOG_UNEXPECTED, LOG_UNEXPECTED_RESULTS))
+LOG_DEFAULT_VALUE = ",".join((LOG_DETAILED_PROGRESS, LOG_SUMMARY,
+ LOG_UNEXPECTED, LOG_UNEXPECTED_RESULTS))
# Builder base URL where we have the archived test results.
BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/"
@@ -98,13 +125,27 @@ class TestInfo:
self.filename = filename
self.uri = port.filename_to_uri(filename)
self.timeout = timeout
- expected_hash_file = port.expected_filename(filename, '.checksum')
+ # FIXME: Confusing that the file is .checksum and we call it "hash"
+ self._expected_hash_path = port.expected_filename(filename, '.checksum')
+ self._have_read_expected_hash = False
+ self._image_hash = None
+
+ def _read_image_hash(self):
try:
- self.image_hash = open(expected_hash_file, "r").read()
+ with codecs.open(self._expected_hash_path, "r", "ascii") as hash_file:
+ return hash_file.read()
except IOError, e:
if errno.ENOENT != e.errno:
raise
- self.image_hash = None
+
+ def image_hash(self):
+ # Read the image_hash lazily to reduce startup time.
+ # This class is accessed across threads, but only one thread should
+ # ever be dealing with any given TestInfo so no locking is needed.
+ if not self._have_read_expected_hash:
+ self._have_read_expected_hash = True
+ self._image_hash = self._read_image_hash()
+ return self._image_hash
class ResultSummary(object):
@@ -131,25 +172,23 @@ class ResultSummary(object):
self.tests_by_timeline[timeline] = (
expectations.get_tests_with_timeline(timeline))
- def add(self, test, failures, result, expected):
- """Add a result into the appropriate bin.
+ def add(self, result, expected):
+ """Add a TestResult into the appropriate bin.
Args:
- test: test file name
- failures: list of failure objects from test execution
- result: result of test (PASS, IMAGE, etc.).
+ result: TestResult from dump_render_tree_thread.
expected: whether the result was what we expected it to be.
"""
- self.tests_by_expectation[result].add(test)
- self.results[test] = result
+ self.tests_by_expectation[result.type].add(result.filename)
+ self.results[result.filename] = result.type
self.remaining -= 1
- if len(failures):
- self.failures[test] = failures
+ if len(result.failures):
+ self.failures[result.filename] = result.failures
if expected:
self.expected += 1
else:
- self.unexpected_results[test] = result
+ self.unexpected_results[result.filename] = result.type
self.unexpected += 1
@@ -162,7 +201,7 @@ class TestRunner:
# The per-test timeout in milliseconds, if no --time-out-ms option was
# given to run_webkit_tests. This should correspond to the default timeout
- # in test_shell.exe.
+ # in DumpRenderTree.
DEFAULT_TEST_TIMEOUT_MS = 6 * 1000
NUM_RETRY_ON_UNEXPECTED_FAILURE = 1
@@ -197,14 +236,16 @@ class TestRunner:
self._current_progress_str = ""
self._current_test_number = 0
+ self._retries = 0
+
def __del__(self):
- logging.debug("flushing stdout")
+ _log.debug("flushing stdout")
sys.stdout.flush()
- logging.debug("flushing stderr")
+ _log.debug("flushing stderr")
sys.stderr.flush()
- logging.debug("stopping http server")
+ _log.debug("stopping http server")
self._port.stop_http_server()
- logging.debug("stopping websocket server")
+ _log.debug("stopping websocket server")
self._port.stop_websocket_server()
def gather_file_paths(self, paths):
@@ -225,11 +266,13 @@ class TestRunner:
try:
expectations_str = self._port.test_expectations()
+ overrides_str = self._port.test_expectations_overrides()
self._expectations = test_expectations.TestExpectations(
self._port, test_files, expectations_str, test_platform_name,
- is_debug_mode, self._options.lint_test_files)
+ is_debug_mode, self._options.lint_test_files,
+ tests_are_present=True, overrides=overrides_str)
return self._expectations
- except Exception, err:
+ except SyntaxError, err:
if self._options.lint_test_files:
print str(err)
else:
@@ -274,7 +317,7 @@ class TestRunner:
test_size = int(chunk_len)
assert(test_size > 0)
except:
- logging.critical("invalid chunk '%s'" % chunk_value)
+ _log.critical("invalid chunk '%s'" % chunk_value)
sys.exit(1)
# Get the number of tests
@@ -343,7 +386,7 @@ class TestRunner:
self._expectations = self.parse_expectations(
self._port.test_platform_name(),
- self._options.target == 'Debug')
+ self._options.configuration == 'Debug')
self._test_files = set(files)
self._test_files_list = files
@@ -361,7 +404,6 @@ class TestRunner:
self._print_expected_results_of_type(write, result_summary,
test_expectations.SKIP, "skipped")
-
if self._options.force:
write('Running all tests, including skips (--force)')
else:
@@ -369,8 +411,11 @@ class TestRunner:
# subtracted out of self._test_files, above), but we stub out the
# results here so the statistics can remain accurate.
for test in skip_chunk:
- result_summary.add(test, [], test_expectations.SKIP,
- expected=True)
+ result = dump_render_tree_thread.TestResult(test,
+ failures=[], test_run_time=0, total_time_for_all_diffs=0,
+ time_for_diffs=0)
+ result.type = test_expectations.SKIP
+ result_summary.add(result, expected=True)
write("")
return result_summary
@@ -471,12 +516,12 @@ class TestRunner:
filename_queue.put(item)
return filename_queue
- def _get_test_shell_args(self, index):
- """Returns the tuple of arguments for tests and for test_shell."""
+ def _get_dump_render_tree_args(self, index):
+ """Returns the tuple of arguments for tests and for DumpRenderTree."""
shell_args = []
test_args = test_type_base.TestArguments()
png_path = None
- if not self._options.no_pixel_tests:
+ if self._options.pixel_tests:
png_path = os.path.join(self._options.results_directory,
"png_result%s.png" % index)
shell_args.append("--pixel-tests=" + png_path)
@@ -495,12 +540,13 @@ class TestRunner:
return test_args, png_path, shell_args
def _contains_tests(self, subdir):
- for test_file in self._test_files_list:
+ for test_file in self._test_files:
if test_file.find(subdir) >= 0:
return True
return False
- def _instantiate_test_shell_threads(self, test_files, result_summary):
+ def _instantiate_dump_render_tree_threads(self, test_files,
+ result_summary):
"""Instantitates and starts the TestShellThread(s).
Return:
@@ -510,22 +556,18 @@ class TestRunner:
# Instantiate TestShellThreads and start them.
threads = []
- for i in xrange(int(self._options.num_test_shells)):
+ for i in xrange(int(self._options.child_processes)):
# Create separate TestTypes instances for each thread.
test_types = []
- for t in self._test_types:
- test_types.append(t(self._port, self._options.platform,
+ for test_type in self._test_types:
+ test_types.append(test_type(self._port,
self._options.results_directory))
- test_args, png_path, shell_args = self._get_test_shell_args(i)
- thread = test_shell_thread.TestShellThread(self._port,
- filename_queue,
- self._result_queue,
- test_types,
- test_args,
- png_path,
- shell_args,
- self._options)
+ test_args, png_path, shell_args = \
+ self._get_dump_render_tree_args(i)
+ thread = dump_render_tree_thread.TestShellThread(self._port,
+ filename_queue, self._result_queue, test_types, test_args,
+ png_path, shell_args, self._options)
if self._is_single_threaded():
thread.run_in_main_thread(self, result_summary)
else:
@@ -536,7 +578,7 @@ class TestRunner:
def _is_single_threaded(self):
"""Returns whether we should run all the tests in the main thread."""
- return int(self._options.num_test_shells) == 1
+ return int(self._options.child_processes) == 1
def _run_tests(self, file_list, result_summary):
"""Runs the tests in the file_list.
@@ -552,8 +594,14 @@ class TestRunner:
in the form {filename:filename, test_run_time:test_run_time}
result_summary: summary object to populate with the results
"""
- threads = self._instantiate_test_shell_threads(file_list,
- result_summary)
+ plural = ""
+ if self._options.child_processes > 1:
+ plural = "s"
+ self._meter.update('Starting %s%s ...' %
+ (self._port.driver_name(), plural))
+ threads = self._instantiate_dump_render_tree_threads(file_list,
+ result_summary)
+ self._meter.update("Starting testing ...")
# Wait for the threads to finish and collect test failures.
failures = {}
@@ -575,11 +623,10 @@ class TestRunner:
'total_time': thread.get_total_time()})
test_timings.update(thread.get_directory_timing_stats())
individual_test_timings.extend(
- thread.get_individual_test_stats())
+ thread.get_test_results())
except KeyboardInterrupt:
for thread in threads:
thread.cancel()
- self._port.stop_helper()
raise
for thread in threads:
# Check whether a TestShellThread died before normal completion.
@@ -594,7 +641,11 @@ class TestRunner:
self.update_summary(result_summary)
return (thread_timings, test_timings, individual_test_timings)
- def run(self, result_summary):
+ def needs_http(self):
+ """Returns whether the test runner needs an HTTP server."""
+ return self._contains_tests(self.HTTP_SUBDIR)
+
+ def run(self, result_summary, print_results):
"""Run all our tests on all our test files.
For each test file, we run each test type. If there are any failures,
@@ -602,22 +653,21 @@ class TestRunner:
Args:
result_summary: a summary object tracking the test results.
+ print_results: whether or not to print the summary at the end
Return:
- We return nonzero if there are regressions compared to the last run.
+ The number of unexpected results (0 == success)
"""
if not self._test_files:
return 0
start_time = time.time()
- # Start up any helper needed
- if not self._options.no_pixel_tests:
- self._port.start_helper()
-
- if self._contains_tests(self.HTTP_SUBDIR):
+ if self.needs_http():
+ self._meter.update('Starting HTTP server ...')
self._port.start_http_server()
if self._contains_tests(self.WEBSOCKET_SUBDIR):
+ self._meter.update('Starting WebSocket server ...')
self._port.start_websocket_server()
# self._websocket_secure_server.Start()
@@ -627,17 +677,17 @@ class TestRunner:
# We exclude the crashes from the list of results to retry, because
# we want to treat even a potentially flaky crash as an error.
failures = self._get_failures(result_summary, include_crashes=False)
- retries = 0
retry_summary = result_summary
- while (retries < self.NUM_RETRY_ON_UNEXPECTED_FAILURE and
+ while (self._retries < self.NUM_RETRY_ON_UNEXPECTED_FAILURE and
len(failures)):
- logging.debug("Retrying %d unexpected failure(s)" % len(failures))
- retries += 1
+ _log.info('')
+ _log.info("Retrying %d unexpected failure(s)" % len(failures))
+ _log.info('')
+ self._retries += 1
retry_summary = ResultSummary(self._expectations, failures.keys())
self._run_tests(failures.keys(), retry_summary)
failures = self._get_failures(retry_summary, include_crashes=True)
- self._port.stop_helper()
end_time = time.time()
write = create_logging_writer(self._options, 'timing')
@@ -660,27 +710,29 @@ class TestRunner:
sys.stdout.flush()
sys.stderr.flush()
- if (LOG_DETAILED_PROGRESS in self._options.log or
- (LOG_UNEXPECTED in self._options.log and
- result_summary.total != result_summary.expected)):
- print
-
# This summary data gets written to stdout regardless of log level
- self._print_one_line_summary(result_summary.total,
- result_summary.expected)
+ # (unless of course we're printing nothing).
+ if print_results:
+ if (LOG_DETAILED_PROGRESS in self._options.log or
+ (LOG_UNEXPECTED in self._options.log and
+ result_summary.total != result_summary.expected)):
+ print
+ if LOG_SUMMARY in self._options.log:
+ self._print_one_line_summary(result_summary.total,
+ result_summary.expected)
unexpected_results = self._summarize_unexpected_results(result_summary,
retry_summary)
- self._print_unexpected_results(unexpected_results)
+ if LOG_UNEXPECTED_RESULTS in self._options.log:
+ self._print_unexpected_results(unexpected_results)
# Write the same data to log files.
self._write_json_files(unexpected_results, result_summary,
individual_test_timings)
- # Write the summary to disk (results.html) and maybe open the
- # test_shell to this file.
+ # Write the summary to disk (results.html) and display it if requested.
wrote_results = self._write_results_html_file(result_summary)
- if not self._options.noshow_results and wrote_results:
+ if self._options.show_results and wrote_results:
self._show_results_html_file()
# Ignore flaky failures and unexpected passes so we don't turn the
@@ -688,32 +740,69 @@ class TestRunner:
return unexpected_results['num_regressions']
def update_summary(self, result_summary):
- """Update the summary while running tests."""
+ """Update the summary and print results with any completed tests."""
while True:
try:
- (test, fail_list) = self._result_queue.get_nowait()
- result = test_failures.determine_result_type(fail_list)
- expected = self._expectations.matches_an_expected_result(test,
- result)
- result_summary.add(test, fail_list, result, expected)
- if (LOG_DETAILED_PROGRESS in self._options.log and
- (self._options.experimental_fully_parallel or
- self._is_single_threaded())):
- self._display_detailed_progress(result_summary)
- else:
- if not expected and LOG_UNEXPECTED in self._options.log:
- self._print_unexpected_test_result(test, result)
- self._display_one_line_progress(result_summary)
+ result = self._result_queue.get_nowait()
except Queue.Empty:
return
-
- def _display_one_line_progress(self, result_summary):
+ expected = self._expectations.matches_an_expected_result(
+ result.filename, result.type, self._options.pixel_tests)
+ result_summary.add(result, expected)
+ self._print_test_results(result, expected, result_summary)
+
+ def _print_test_results(self, result, expected, result_summary):
+ "Print the result of the test as determined by the --log switches."
+ if LOG_TRACE in self._options.log:
+ self._print_test_trace(result)
+ elif (LOG_DETAILED_PROGRESS in self._options.log and
+ (self._options.experimental_fully_parallel or
+ self._is_single_threaded())):
+ self._print_detailed_progress(result_summary)
+ else:
+ if (not expected and LOG_UNEXPECTED in self._options.log):
+ self._print_unexpected_test_result(result)
+ self._print_one_line_progress(result_summary)
+
+ def _print_test_trace(self, result):
+ """Print detailed results of a test (triggered by --log trace).
+ For each test, print:
+ - location of the expected baselines
+ - expected results
+ - actual result
+ - timing info
+ """
+ filename = result.filename
+ test_name = self._port.relative_test_filename(filename)
+ _log.info('trace: %s' % test_name)
+ _log.info(' txt: %s' %
+ self._port.relative_test_filename(
+ self._port.expected_filename(filename, '.txt')))
+ png_file = self._port.expected_filename(filename, '.png')
+ if os.path.exists(png_file):
+ _log.info(' png: %s' %
+ self._port.relative_test_filename(filename))
+ else:
+ _log.info(' png: <none>')
+ _log.info(' exp: %s' %
+ self._expectations.get_expectations_string(filename))
+ _log.info(' got: %s' %
+ self._expectations.expectation_to_string(result.type))
+ _log.info(' took: %-.3f' % result.test_run_time)
+ _log.info('')
+
+ def _print_one_line_progress(self, result_summary):
"""Displays the progress through the test run."""
- self._meter.update("Testing: %d ran as expected, %d didn't, %d left" %
- (result_summary.expected, result_summary.unexpected,
- result_summary.remaining))
-
- def _display_detailed_progress(self, result_summary):
+ percent_complete = 100 * (result_summary.expected +
+ result_summary.unexpected) / result_summary.total
+ action = "Testing"
+ if self._retries > 0:
+ action = "Retrying"
+ self._meter.progress("%s (%d%%): %d ran as expected, %d didn't,"
+ " %d left" % (action, percent_complete, result_summary.expected,
+ result_summary.unexpected, result_summary.remaining))
+
+ def _print_detailed_progress(self, result_summary):
"""Display detailed progress output where we print the directory name
and one dot for each completed test. This is triggered by
"--log detailed-progress"."""
@@ -752,10 +841,17 @@ class TestRunner:
if result_summary.remaining:
remain_str = " (%d)" % (result_summary.remaining)
- self._meter.update("%s%s" %
- (self._current_progress_str, remain_str))
+ self._meter.progress("%s%s" %
+ (self._current_progress_str, remain_str))
else:
- self._meter.write("%s\n" % (self._current_progress_str))
+ self._meter.progress("%s\n" % (self._current_progress_str))
+
+ def _print_unexpected_test_result(self, result):
+ """Prints one unexpected test result line."""
+ desc = TestExpectationsFile.EXPECTATION_DESCRIPTIONS[result.type][0]
+ self._meter.write(" %s -> unexpected %s\n" %
+ (self._port.relative_test_filename(result.filename),
+ desc))
def _get_failures(self, result_summary, include_crashes):
"""Filters a dict of results and returns only the failures.
@@ -870,8 +966,8 @@ class TestRunner:
individual_test_timings: list of test times (used by the flakiness
dashboard).
"""
- logging.debug("Writing JSON files in %s." %
- self._options.results_directory)
+ _log.debug("Writing JSON files in %s." %
+ self._options.results_directory)
unexpected_file = open(os.path.join(self._options.results_directory,
"unexpected_results.json"), "w")
unexpected_file.write(simplejson.dumps(unexpected_results,
@@ -893,7 +989,7 @@ class TestRunner:
BUILDER_BASE_URL, individual_test_timings,
self._expectations, result_summary, self._test_files_list)
- logging.debug("Finished writing JSON files.")
+ _log.debug("Finished writing JSON files.")
def _print_expected_results_of_type(self, write, result_summary,
result_type, result_type_str):
@@ -951,7 +1047,7 @@ class TestRunner:
(t['name'], t['num_tests'], t['total_time']))
cuml_time += t['total_time']
write(" %6.2f cumulative, %6.2f optimal" %
- (cuml_time, cuml_time / int(self._options.num_test_shells)))
+ (cuml_time, cuml_time / int(self._options.child_processes)))
write("")
self._print_aggregate_test_statistics(write, individual_test_timings)
@@ -964,18 +1060,20 @@ class TestRunner:
Args:
write: A callback to write info to (e.g., a LoggingWriter) or
sys.stdout.write.
- individual_test_timings: List of test_shell_thread.TestStats for all
- tests.
+ individual_test_timings: List of dump_render_tree_thread.TestStats
+ for all tests.
"""
- test_types = individual_test_timings[0].time_for_diffs.keys()
- times_for_test_shell = []
+ test_types = [] # Unit tests don't actually produce any timings.
+ if individual_test_timings:
+ test_types = individual_test_timings[0].time_for_diffs.keys()
+ times_for_dump_render_tree = []
times_for_diff_processing = []
times_per_test_type = {}
for test_type in test_types:
times_per_test_type[test_type] = []
for test_stats in individual_test_timings:
- times_for_test_shell.append(test_stats.test_run_time)
+ times_for_dump_render_tree.append(test_stats.test_run_time)
times_for_diff_processing.append(
test_stats.total_time_for_all_diffs)
time_for_diffs = test_stats.time_for_diffs
@@ -984,7 +1082,8 @@ class TestRunner:
time_for_diffs[test_type])
self._print_statistics_for_test_timings(write,
- "PER TEST TIME IN TESTSHELL (seconds):", times_for_test_shell)
+ "PER TEST TIME IN TESTSHELL (seconds):",
+ times_for_dump_render_tree)
self._print_statistics_for_test_timings(write,
"PER TEST DIFF PROCESSING TIMES (seconds):",
times_for_diff_processing)
@@ -999,11 +1098,11 @@ class TestRunner:
Args:
write: A callback to write info to (e.g., a LoggingWriter) or
sys.stdout.write.
- individual_test_timings: List of test_shell_thread.TestStats for all
- tests.
+ individual_test_timings: List of dump_render_tree_thread.TestStats
+ for all tests.
result_summary: summary object for test run
"""
- # Reverse-sort by the time spent in test_shell.
+ # Reverse-sort by the time spent in DumpRenderTree.
individual_test_timings.sort(lambda a, b:
cmp(b.test_run_time, a.test_run_time))
@@ -1098,6 +1197,8 @@ class TestRunner:
timings.sort()
num_tests = len(timings)
+ if not num_tests:
+ return
percentile90 = timings[int(.9 * num_tests)]
percentile99 = timings[int(.99 * num_tests)]
@@ -1269,12 +1370,6 @@ class TestRunner:
if len(unexpected_results['tests']) and self._options.verbose:
print "-" * 78
- def _print_unexpected_test_result(self, test, result):
- """Prints one unexpected test result line."""
- desc = TestExpectationsFile.EXPECTATION_DESCRIPTIONS[result][0]
- self._meter.write(" %s -> unexpected %s\n" %
- (self._port.relative_test_filename(test), desc))
-
def _write_results_html_file(self, result_summary):
"""Write results.html which is a summary of tests that failed.
@@ -1324,7 +1419,7 @@ class TestRunner:
return True
def _show_results_html_file(self):
- """Launches the test shell open to the results.html page."""
+ """Shows the results.html page."""
results_filename = os.path.join(self._options.results_directory,
"results.html")
self._port.show_results_html_file(results_filename)
@@ -1345,7 +1440,7 @@ def read_test_files(files):
def create_logging_writer(options, log_option):
- """Returns a write() function that will write the string to logging.info()
+ """Returns a write() function that will write the string to _log.info()
if comp was specified in --log or if --verbose is true. Otherwise the
message is dropped.
@@ -1355,16 +1450,21 @@ def create_logging_writer(options, log_option):
to be logged (e.g., 'actual' or 'expected')
"""
if options.verbose or log_option in options.log.split(","):
- return logging.info
+ return _log.info
return lambda str: 1
-def main(options, args):
- """Run the tests. Will call sys.exit when complete.
+def main(options, args, print_results=True):
+ """Run the tests.
Args:
options: a dictionary of command line options
args: a list of sub directories or files to test
+ print_results: whether or not to log anything to stdout.
+ Set to false by the unit tests
+ Returns:
+ the number of unexpected results that occurred, or -1 if there is an
+ error.
"""
if options.sources:
@@ -1382,13 +1482,14 @@ def main(options, args):
logging.basicConfig(level=log_level, format=log_fmt, datefmt=log_datefmt,
stream=meter)
- if not options.target:
- if options.debug:
- options.target = "Debug"
- else:
- options.target = "Release"
-
port_obj = port.get(options.platform, options)
+ executive = Executive()
+
+ if not options.configuration:
+ options.configuration = port_obj.default_configuration()
+
+ if options.pixel_tests is None:
+ options.pixel_tests = True
if not options.use_apache:
options.use_apache = sys.platform in ('darwin', 'linux2')
@@ -1402,23 +1503,44 @@ def main(options, args):
# Debug or Release.
options.results_directory = port_obj.results_directory()
+ last_unexpected_results = []
+ if options.print_unexpected_results or options.retry_unexpected_results:
+ unexpected_results_filename = os.path.join(
+ options.results_directory, "unexpected_results.json")
+ f = file(unexpected_results_filename)
+ results = simplejson.load(f)
+ f.close()
+ last_unexpected_results = results['tests'].keys()
+ if options.print_unexpected_results:
+ print "\n".join(last_unexpected_results) + "\n"
+ return 0
+
if options.clobber_old_results:
# Just clobber the actual test results directories since the other
# files in the results directory are explicitly used for cross-run
# tracking.
- path = os.path.join(options.results_directory, 'LayoutTests')
- if os.path.exists(path):
- shutil.rmtree(path)
-
- if not options.num_test_shells:
- # TODO(ojan): Investigate perf/flakiness impact of using numcores + 1.
- options.num_test_shells = port_obj.num_cores()
+ meter.update("Clobbering old results in %s" %
+ options.results_directory)
+ layout_tests_dir = port_obj.layout_tests_dir()
+ possible_dirs = os.listdir(layout_tests_dir)
+ for dirname in possible_dirs:
+ if os.path.isdir(os.path.join(layout_tests_dir, dirname)):
+ shutil.rmtree(os.path.join(options.results_directory, dirname),
+ ignore_errors=True)
+
+ if not options.child_processes:
+ # FIXME: Investigate perf/flakiness impact of using cpu_count + 1.
+ options.child_processes = port_obj.default_child_processes()
write = create_logging_writer(options, 'config')
- write("Running %s test_shells in parallel" % options.num_test_shells)
+ if options.child_processes == 1:
+ write("Running one %s" % port_obj.driver_name)
+ else:
+ write("Running %s %ss in parallel" % (
+ options.child_processes, port_obj.driver_name()))
if not options.time_out_ms:
- if options.target == "Debug":
+ if options.configuration == "Debug":
options.time_out_ms = str(2 * TestRunner.DEFAULT_TEST_TIMEOUT_MS)
else:
options.time_out_ms = str(TestRunner.DEFAULT_TEST_TIMEOUT_MS)
@@ -1436,44 +1558,57 @@ def main(options, args):
paths = new_args
if not paths:
paths = []
+ paths += last_unexpected_results
if options.test_list:
paths += read_test_files(options.test_list)
# Create the output directory if it doesn't already exist.
port_obj.maybe_make_directory(options.results_directory)
- meter.update("Gathering files ...")
+ meter.update("Collecting tests ...")
test_runner = TestRunner(port_obj, options, meter)
test_runner.gather_file_paths(paths)
if options.lint_test_files:
- # Creating the expecations for each platform/target pair does all the
- # test list parsing and ensures it's correct syntax (e.g. no dupes).
- for platform in port_obj.test_platform_names():
- test_runner.parse_expectations(platform, is_debug_mode=True)
- test_runner.parse_expectations(platform, is_debug_mode=False)
+ # Creating the expecations for each platform/configuration pair does
+ # all the test list parsing and ensures it's correct syntax (e.g. no
+ # dupes).
+ for platform_name in port_obj.test_platform_names():
+ test_runner.parse_expectations(platform_name, is_debug_mode=True)
+ test_runner.parse_expectations(platform_name, is_debug_mode=False)
+ meter.update("")
print ("If there are no fail messages, errors or exceptions, then the "
"lint succeeded.")
- sys.exit(0)
-
- # Check that the system dependencies (themes, fonts, ...) are correct.
- if not options.nocheck_sys_deps:
- if not port_obj.check_sys_deps():
- sys.exit(1)
+ return 0
write = create_logging_writer(options, "config")
write("Using port '%s'" % port_obj.name())
write("Placing test results in %s" % options.results_directory)
if options.new_baseline:
write("Placing new baselines in %s" % port_obj.baseline_path())
- write("Using %s build" % options.target)
- if options.no_pixel_tests:
- write("Not running pixel tests")
+ write("Using %s build" % options.configuration)
+ if options.pixel_tests:
+ write("Pixel tests enabled")
+ else:
+ write("Pixel tests disabled")
write("")
meter.update("Parsing expectations ...")
test_runner.parse_expectations(port_obj.test_platform_name(),
- options.target == 'Debug')
+ options.configuration == 'Debug')
+
+ meter.update("Checking build ...")
+ if not port_obj.check_build(test_runner.needs_http()):
+ return -1
+
+ meter.update("Starting helper ...")
+ port_obj.start_helper()
+
+ # Check that the system dependencies (themes, fonts, ...) are correct.
+ if not options.nocheck_sys_deps:
+ meter.update("Checking system dependencies ...")
+ if not port_obj.check_sys_deps(test_runner.needs_http()):
+ return -1
meter.update("Preparing tests ...")
write = create_logging_writer(options, "expected")
@@ -1482,143 +1617,237 @@ def main(options, args):
port_obj.setup_test_run()
test_runner.add_test_type(text_diff.TestTextDiff)
- if not options.no_pixel_tests:
+ if options.pixel_tests:
test_runner.add_test_type(image_diff.ImageDiff)
if options.fuzzy_pixel_tests:
test_runner.add_test_type(fuzzy_image_diff.FuzzyImageDiff)
- meter.update("Starting ...")
- has_new_failures = test_runner.run(result_summary)
+ num_unexpected_results = test_runner.run(result_summary, print_results)
- logging.debug("Exit status: %d" % has_new_failures)
- sys.exit(has_new_failures)
+ port_obj.stop_helper()
+
+ _log.debug("Exit status: %d" % num_unexpected_results)
+ return num_unexpected_results
+
+
+def _compat_shim_callback(option, opt_str, value, parser):
+ print "Ignoring unsupported option: %s" % opt_str
+
+
+def _compat_shim_option(option_name, nargs=0):
+ return optparse.make_option(option_name, action="callback", callback=_compat_shim_callback, nargs=nargs, help="Ignored, for old-run-webkit-tests compat only.")
def parse_args(args=None):
"""Provides a default set of command line args.
Returns a tuple of options, args from optparse"""
- option_parser = optparse.OptionParser()
- option_parser.add_option("", "--no-pixel-tests", action="store_true",
- default=False,
- help="disable pixel-to-pixel PNG comparisons")
- option_parser.add_option("", "--fuzzy-pixel-tests", action="store_true",
- default=False,
- help="Also use fuzzy matching to compare pixel "
- "test outputs.")
- option_parser.add_option("", "--results-directory",
- default="layout-test-results",
- help="Output results directory source dir,"
- " relative to Debug or Release")
- option_parser.add_option("", "--new-baseline", action="store_true",
- default=False,
- help="save all generated results as new baselines"
- " into the platform directory, overwriting "
- "whatever's already there.")
- option_parser.add_option("", "--noshow-results", action="store_true",
- default=False, help="don't launch the test_shell"
- " with results after the tests are done")
- option_parser.add_option("", "--full-results-html", action="store_true",
- default=False, help="show all failures in "
- "results.html, rather than only regressions")
- option_parser.add_option("", "--clobber-old-results", action="store_true",
- default=False, help="Clobbers test results from "
- "previous runs.")
- option_parser.add_option("", "--lint-test-files", action="store_true",
- default=False, help="Makes sure the test files "
- "parse for all configurations. Does not run any "
- "tests.")
- option_parser.add_option("", "--force", action="store_true",
- default=False,
- help="Run all tests, even those marked SKIP "
- "in the test list")
- option_parser.add_option("", "--num-test-shells",
- help="Number of testshells to run in parallel.")
- option_parser.add_option("", "--use-apache", action="store_true",
- default=False,
- help="Whether to use apache instead of lighttpd.")
- option_parser.add_option("", "--time-out-ms", default=None,
- help="Set the timeout for each test")
- option_parser.add_option("", "--run-singly", action="store_true",
- default=False,
- help="run a separate test_shell for each test")
- option_parser.add_option("", "--debug", action="store_true", default=False,
- help="use the debug binary instead of the release"
- " binary")
- option_parser.add_option("", "--num-slow-tests-to-log", default=50,
- help="Number of slow tests whose timings "
- "to print.")
- option_parser.add_option("", "--platform",
- help="Override the platform for expected results")
- option_parser.add_option("", "--target", default="",
- help="Set the build target configuration "
- "(overrides --debug)")
- option_parser.add_option("", "--log", action="store",
- default="detailed-progress,unexpected",
- help="log various types of data. The param should"
- " be a comma-separated list of values from: "
- "actual,config," + LOG_DETAILED_PROGRESS +
- ",expected,timing," + LOG_UNEXPECTED + " "
- "(defaults to " +
- "--log detailed-progress,unexpected)")
- option_parser.add_option("-v", "--verbose", action="store_true",
- default=False, help="include debug-level logging")
- option_parser.add_option("", "--sources", action="store_true",
- help="show expected result file path for each "
- "test (implies --verbose)")
- option_parser.add_option("", "--startup-dialog", action="store_true",
- default=False,
- help="create a dialog on test_shell.exe startup")
- option_parser.add_option("", "--gp-fault-error-box", action="store_true",
- default=False,
- help="enable Windows GP fault error box")
- option_parser.add_option("", "--wrapper",
- help="wrapper command to insert before "
- "invocations of test_shell; option is split "
- "on whitespace before running. (Example: "
- "--wrapper='valgrind --smc-check=all')")
- option_parser.add_option("", "--test-list", action="append",
- help="read list of tests to run from file",
- metavar="FILE")
- option_parser.add_option("", "--nocheck-sys-deps", action="store_true",
- default=False,
- help="Don't check the system dependencies "
- "(themes)")
- option_parser.add_option("", "--randomize-order", action="store_true",
- default=False,
- help=("Run tests in random order (useful for "
- "tracking down corruption)"))
- option_parser.add_option("", "--run-chunk",
- default=None,
- help=("Run a specified chunk (n:l), the "
- "nth of len l, of the layout tests"))
- option_parser.add_option("", "--run-part",
- default=None,
- help=("Run a specified part (n:m), the nth of m"
- " parts, of the layout tests"))
- option_parser.add_option("", "--batch-size",
- default=None,
- help=("Run a the tests in batches (n), after "
- "every n tests, the test shell is "
- "relaunched."))
- option_parser.add_option("", "--builder-name",
- default="DUMMY_BUILDER_NAME",
- help=("The name of the builder shown on the "
- "waterfall running this script e.g. "
- "WebKit."))
- option_parser.add_option("", "--build-name",
- default="DUMMY_BUILD_NAME",
- help=("The name of the builder used in its path, "
- "e.g. webkit-rel."))
- option_parser.add_option("", "--build-number",
- default="DUMMY_BUILD_NUMBER",
- help=("The build number of the builder running"
- "this script."))
- option_parser.add_option("", "--experimental-fully-parallel",
- action="store_true", default=False,
- help="run all tests in parallel")
+
+ # FIXME: All of these options should be stored closer to the code which
+ # FIXME: actually uses them. configuration_options should move
+ # FIXME: to WebKitPort and be shared across all scripts.
+ configuration_options = [
+ optparse.make_option("-t", "--target", dest="configuration",
+ help="(DEPRECATED)"),
+ # FIXME: --help should display which configuration is default.
+ optparse.make_option('--debug', action='store_const', const='Debug',
+ dest="configuration",
+ help='Set the configuration to Debug'),
+ optparse.make_option('--release', action='store_const',
+ const='Release', dest="configuration",
+ help='Set the configuration to Release'),
+ # old-run-webkit-tests also accepts -c, --configuration CONFIGURATION.
+ ]
+
+ logging_options = [
+ optparse.make_option("--log", action="store",
+ default=LOG_DEFAULT_VALUE,
+ help=("log various types of data. The argument value should be a "
+ "comma-separated list of values from: %s (defaults to "
+ "--log %s)" % (LOG_VALUES, LOG_DEFAULT_VALUE))),
+ optparse.make_option("-v", "--verbose", action="store_true",
+ default=False, help="include debug-level logging"),
+ optparse.make_option("--sources", action="store_true",
+ help="show expected result file path for each test " +
+ "(implies --verbose)"),
+ # old-run-webkit-tests has a --slowest option which just prints
+ # the slowest 10.
+ optparse.make_option("--num-slow-tests-to-log", default=50,
+ help="Number of slow tests whose timings to print."),
+ ]
+
+ # FIXME: These options should move onto the ChromiumPort.
+ chromium_options = [
+ optparse.make_option("--chromium", action="store_true", default=False,
+ help="use the Chromium port"),
+ optparse.make_option("--startup-dialog", action="store_true",
+ default=False, help="create a dialog on DumpRenderTree startup"),
+ optparse.make_option("--gp-fault-error-box", action="store_true",
+ default=False, help="enable Windows GP fault error box"),
+ optparse.make_option("--nocheck-sys-deps", action="store_true",
+ default=False,
+ help="Don't check the system dependencies (themes)"),
+ optparse.make_option("--use-drt", action="store_true",
+ default=False,
+ help="Use DumpRenderTree instead of test_shell"),
+ ]
+
+ # Missing Mac-specific old-run-webkit-tests options:
+ # FIXME: Need: -g, --guard for guard malloc support on Mac.
+ # FIXME: Need: -l --leaks Enable leaks checking.
+ # FIXME: Need: --sample-on-timeout Run sample on timeout
+
+ old_run_webkit_tests_compat = [
+ # NRWT doesn't generate results by default anyway.
+ _compat_shim_option("--no-new-test-results"),
+ # NRWT doesn't sample on timeout yet anyway.
+ _compat_shim_option("--no-sample-on-timeout"),
+ # FIXME: NRWT needs to support remote links eventually.
+ _compat_shim_option("--use-remote-links-to-tests"),
+ # FIXME: NRWT doesn't need this option as much since failures are
+ # designed to be cheap. We eventually plan to add this support.
+ _compat_shim_option("--exit-after-n-failures", nargs=1),
+ ]
+
+ results_options = [
+ # NEED for bots: --use-remote-links-to-tests Link to test files
+ # within the SVN repository in the results.
+ optparse.make_option("-p", "--pixel-tests", action="store_true",
+ dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
+ optparse.make_option("--no-pixel-tests", action="store_false",
+ dest="pixel_tests", help="Disable pixel-to-pixel PNG comparisons"),
+ optparse.make_option("--fuzzy-pixel-tests", action="store_true",
+ default=False,
+ help="Also use fuzzy matching to compare pixel test outputs."),
+ # old-run-webkit-tests allows a specific tolerance: --tolerance t
+ # Ignore image differences less than this percentage (default: 0.1)
+ optparse.make_option("--results-directory",
+ default="layout-test-results",
+ help="Output results directory source dir, relative to Debug or "
+ "Release"),
+ optparse.make_option("--new-baseline", action="store_true",
+ default=False, help="Save all generated results as new baselines "
+ "into the platform directory, overwriting whatever's "
+ "already there."),
+ optparse.make_option("--no-show-results", action="store_false",
+ default=True, dest="show_results",
+ help="Don't launch a browser with results after the tests "
+ "are done"),
+ # FIXME: We should have a helper function to do this sort of
+ # deprectated mapping and automatically log, etc.
+ optparse.make_option("--noshow-results", action="store_false",
+ dest="show_results",
+ help="Deprecated, same as --no-show-results."),
+ optparse.make_option("--no-launch-safari", action="store_false",
+ dest="show_results",
+ help="old-run-webkit-tests compat, same as --noshow-results."),
+ # old-run-webkit-tests:
+ # --[no-]launch-safari Launch (or do not launch) Safari to display
+ # test results (default: launch)
+ optparse.make_option("--full-results-html", action="store_true",
+ default=False,
+ help="Show all failures in results.html, rather than only "
+ "regressions"),
+ optparse.make_option("--clobber-old-results", action="store_true",
+ default=False, help="Clobbers test results from previous runs."),
+ optparse.make_option("--platform",
+ help="Override the platform for expected results"),
+ # old-run-webkit-tests also has HTTP toggle options:
+ # --[no-]http Run (or do not run) http tests
+ # (default: run)
+ # --[no-]wait-for-httpd Wait for httpd if some other test
+ # session is using it already (same
+ # as WEBKIT_WAIT_FOR_HTTPD=1).
+ # (default: 0)
+ ]
+
+ test_options = [
+ optparse.make_option("--build", dest="build",
+ action="store_true", default=True,
+ help="Check to ensure the DumpRenderTree build is up-to-date "
+ "(default)."),
+ optparse.make_option("--no-build", dest="build",
+ action="store_false", help="Don't check to see if the "
+ "DumpRenderTree build is up-to-date."),
+ # old-run-webkit-tests has --valgrind instead of wrapper.
+ optparse.make_option("--wrapper",
+ help="wrapper command to insert before invocations of "
+ "DumpRenderTree; option is split on whitespace before "
+ "running. (Example: --wrapper='valgrind --smc-check=all')"),
+ # old-run-webkit-tests:
+ # -i|--ignore-tests Comma-separated list of directories
+ # or tests to ignore
+ optparse.make_option("--test-list", action="append",
+ help="read list of tests to run from file", metavar="FILE"),
+ # old-run-webkit-tests uses --skipped==[default|ignore|only]
+ # instead of --force:
+ optparse.make_option("--force", action="store_true", default=False,
+ help="Run all tests, even those marked SKIP in the test list"),
+ optparse.make_option("--use-apache", action="store_true",
+ default=False, help="Whether to use apache instead of lighttpd."),
+ optparse.make_option("--time-out-ms",
+ help="Set the timeout for each test"),
+ # old-run-webkit-tests calls --randomize-order --random:
+ optparse.make_option("--randomize-order", action="store_true",
+ default=False, help=("Run tests in random order (useful "
+ "for tracking down corruption)")),
+ optparse.make_option("--run-chunk",
+ help=("Run a specified chunk (n:l), the nth of len l, "
+ "of the layout tests")),
+ optparse.make_option("--run-part", help=("Run a specified part (n:m), "
+ "the nth of m parts, of the layout tests")),
+ # old-run-webkit-tests calls --batch-size: --nthly n
+ # Restart DumpRenderTree every n tests (default: 1000)
+ optparse.make_option("--batch-size",
+ help=("Run a the tests in batches (n), after every n tests, "
+ "DumpRenderTree is relaunched.")),
+ # old-run-webkit-tests calls --run-singly: -1|--singly
+ # Isolate each test case run (implies --nthly 1 --verbose)
+ optparse.make_option("--run-singly", action="store_true",
+ default=False, help="run a separate DumpRenderTree for each test"),
+ optparse.make_option("--child-processes",
+ help="Number of DumpRenderTrees to run in parallel."),
+ # FIXME: Display default number of child processes that will run.
+ optparse.make_option("--experimental-fully-parallel",
+ action="store_true", default=False,
+ help="run all tests in parallel"),
+ # FIXME: Need --exit-after-n-failures N
+ # Exit after the first N failures instead of running all tests
+ # FIXME: consider: --iterations n
+ # Number of times to run the set of tests (e.g. ABCABCABC)
+ optparse.make_option("--print-unexpected-results", action="store_true",
+ default=False, help="print the tests in the last run that "
+ "had unexpected results."),
+ optparse.make_option("--retry-unexpected-results", action="store_true",
+ default=False, help="re-try the tests in the last run that "
+ "had unexpected results."),
+ ]
+
+ misc_options = [
+ optparse.make_option("--lint-test-files", action="store_true",
+ default=False, help=("Makes sure the test files parse for all "
+ "configurations. Does not run any tests.")),
+ ]
+
+ # FIXME: Move these into json_results_generator.py
+ results_json_options = [
+ optparse.make_option("--builder-name", default="DUMMY_BUILDER_NAME",
+ help=("The name of the builder shown on the waterfall running "
+ "this script e.g. WebKit.")),
+ optparse.make_option("--build-name", default="DUMMY_BUILD_NAME",
+ help=("The name of the builder used in its path, e.g. "
+ "webkit-rel.")),
+ optparse.make_option("--build-number", default="DUMMY_BUILD_NUMBER",
+ help=("The build number of the builder running this script.")),
+ ]
+
+ option_list = (configuration_options + logging_options +
+ chromium_options + results_options + test_options +
+ misc_options + results_json_options +
+ old_run_webkit_tests_compat)
+ option_parser = optparse.OptionParser(option_list=option_list)
return option_parser.parse_args(args)
if '__main__' == __name__:
options, args = parse_args()
- main(options, args)
+ sys.exit(main(options, args))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
new file mode 100644
index 0000000..9fe0e74
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+# Copyright (C) 2010 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.
+
+"""Unit tests for run_webkit_tests."""
+
+import os
+import sys
+import unittest
+
+import webkitpy.layout_tests.run_webkit_tests as run_webkit_tests
+
+
+def passing_run(args):
+ options, args = run_webkit_tests.parse_args(args)
+ res = run_webkit_tests.main(options, args, False)
+ return res == 0
+
+
+class MainTest(unittest.TestCase):
+ def test_fast(self):
+ self.assertTrue(passing_run(['--platform', 'test',
+ 'fast/html']))
+ self.assertTrue(passing_run(['--platform', 'test',
+ '--run-singly',
+ 'fast/html']))
+ self.assertTrue(passing_run(['--platform', 'test',
+ 'fast/html/article-element.html']))
+ self.assertTrue(passing_run(['--platform', 'test',
+ '--child-processes', '1',
+ '--log', 'unexpected',
+ 'fast/html']))
+
+
+class DryrunTest(unittest.TestCase):
+ def test_basics(self):
+ self.assertTrue(passing_run(['--platform', 'dryrun',
+ 'fast/html']))
+ #self.assertTrue(passing_run(['--platform', 'dryrun-mac',
+ # 'fast/html']))
+ #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-mac',
+ # 'fast/html']))
+ #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-win',
+ # 'fast/html']))
+ #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-linux',
+ # 'fast/html']))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py
index 89dd192..64dfb20 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/fuzzy_image_diff.py
@@ -36,13 +36,15 @@ import logging
import os
import shutil
-from layout_package import test_failures
-from test_types import test_type_base
+from webkitpy.layout_tests.layout_package import test_failures
+from webkitpy.layout_tests.test_types import test_type_base
+
+_log = logging.getLogger("webkitpy.layout_tests.test_types.fuzzy_image_diff")
class FuzzyImageDiff(test_type_base.TestTypeBase):
- def compare_output(self, filename, output, test_args, target):
+ def compare_output(self, filename, output, test_args, configuration):
"""Implementation of CompareOutput that checks the output image and
checksum against the expected files from the LayoutTest directory.
"""
@@ -55,14 +57,14 @@ class FuzzyImageDiff(test_type_base.TestTypeBase):
expected_png_file = self._port.expected_filename(filename, '.png')
if test_args.show_sources:
- logging.debug('Using %s' % expected_png_file)
+ _log.debug('Using %s' % expected_png_file)
# Also report a missing expected PNG file.
if not os.path.isfile(expected_png_file):
failures.append(test_failures.FailureMissingImage(self))
# Run the fuzzymatcher
- r = port.fuzzy_diff(test_args.png_path, expected_png_file)
+ r = self._port.fuzzy_diff(test_args.png_path, expected_png_file)
if r != 0:
failures.append(test_failures.FailureFuzzyFailure(self))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py
index 1df7ca3..b414358 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py
@@ -39,13 +39,15 @@ import logging
import os
import shutil
-from layout_package import test_failures
-from test_types import test_type_base
+from webkitpy.layout_tests.layout_package import test_failures
+from webkitpy.layout_tests.test_types import test_type_base
# Cache whether we have the image_diff executable available.
_compare_available = True
_compare_msg_printed = False
+_log = logging.getLogger("webkitpy.layout_tests.test_types.image_diff")
+
class ImageDiff(test_type_base.TestTypeBase):
@@ -82,12 +84,13 @@ class ImageDiff(test_type_base.TestTypeBase):
self._save_baseline_data(filename, png_data, ".png")
self._save_baseline_data(filename, checksum, ".checksum")
- def _create_image_diff(self, port, filename, target):
+ def _create_image_diff(self, port, filename, configuration):
"""Creates the visual diff of the expected/actual PNGs.
Args:
filename: the name of the test
- target: Debug or Release
+ configuration: Debug or Release
+ Returns True if the files are different, False if they match
"""
diff_filename = self.output_filename(filename,
self.FILENAME_SUFFIX_COMPARE)
@@ -96,9 +99,10 @@ class ImageDiff(test_type_base.TestTypeBase):
expected_filename = self.output_filename(filename,
self.FILENAME_SUFFIX_EXPECTED + '.png')
+ result = True
try:
_compare_available = True
- result = port.diff_image(actual_filename, expected_filename,
+ result = port.diff_image(expected_filename, actual_filename,
diff_filename)
except ValueError:
_compare_available = False
@@ -106,12 +110,12 @@ class ImageDiff(test_type_base.TestTypeBase):
global _compare_msg_printed
if not _compare_available and not _compare_msg_printed:
_compare_msg_printed = True
- print('image_diff not found. Make sure you have a ' + target +
- ' build of the image_diff executable.')
+ print('image_diff not found. Make sure you have a ' +
+ configuration + ' build of the image_diff executable.')
return result
- def compare_output(self, port, filename, output, test_args, target):
+ def compare_output(self, port, filename, output, test_args, configuration):
"""Implementation of CompareOutput that checks the output image and
checksum against the expected files from the LayoutTest directory.
"""
@@ -133,8 +137,8 @@ class ImageDiff(test_type_base.TestTypeBase):
expected_png_file = self._port.expected_filename(filename, '.png')
if test_args.show_sources:
- logging.debug('Using %s' % expected_hash_file)
- logging.debug('Using %s' % expected_png_file)
+ _log.debug('Using %s' % expected_hash_file)
+ _log.debug('Using %s' % expected_png_file)
try:
expected_hash = open(expected_hash_file, "r").read()
@@ -146,9 +150,9 @@ class ImageDiff(test_type_base.TestTypeBase):
if not os.path.isfile(expected_png_file):
# Report a missing expected PNG file.
- self.write_output_files(port, filename, '', '.checksum',
+ self.write_output_files(port, filename, '.checksum',
test_args.hash, expected_hash,
- diff=False, wdiff=False)
+ print_text_diffs=False)
self._copy_output_png(filename, test_args.png_path, '-actual.png')
failures.append(test_failures.FailureMissingImage(self))
return failures
@@ -156,25 +160,22 @@ class ImageDiff(test_type_base.TestTypeBase):
# Hash matched (no diff needed, okay to return).
return failures
-
- self.write_output_files(port, filename, '', '.checksum',
+ self.write_output_files(port, filename, '.checksum',
test_args.hash, expected_hash,
- diff=False, wdiff=False)
+ print_text_diffs=False)
self._copy_output_png(filename, test_args.png_path, '-actual.png')
self._copy_output_png(filename, expected_png_file, '-expected.png')
- # Even though we only use result in one codepath below but we
+ # Even though we only use the result in one codepath below but we
# still need to call CreateImageDiff for other codepaths.
- result = self._create_image_diff(port, filename, target)
+ images_are_different = self._create_image_diff(port, filename, configuration)
if expected_hash == '':
failures.append(test_failures.FailureMissingImageHash(self))
elif test_args.hash != expected_hash:
- # Hashes don't match, so see if the images match. If they do, then
- # the hash is wrong.
- if result == 0:
- failures.append(test_failures.FailureImageHashIncorrect(self))
- else:
+ if images_are_different:
failures.append(test_failures.FailureImageHashMismatch(self))
+ else:
+ failures.append(test_failures.FailureImageHashIncorrect(self))
return failures
@@ -188,10 +189,7 @@ class ImageDiff(test_type_base.TestTypeBase):
True if two files are different.
False otherwise.
"""
-
try:
- result = port.diff_image(file1, file2)
+ return port.diff_image(file1, file2)
except ValueError, e:
return True
-
- return result == 1
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py
index efa2e8c..4c99be0 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py
@@ -37,6 +37,8 @@ import errno
import logging
import os.path
+_log = logging.getLogger("webkitpy.layout_tests.test_types.test_type_base")
+
class TestArguments(object):
"""Struct-like wrapper for additional arguments needed by
@@ -68,19 +70,18 @@ class TestTypeBase(object):
FILENAME_SUFFIX_EXPECTED = "-expected"
FILENAME_SUFFIX_DIFF = "-diff"
FILENAME_SUFFIX_WDIFF = "-wdiff.html"
+ FILENAME_SUFFIX_PRETTY_PATCH = "-pretty-diff.html"
FILENAME_SUFFIX_COMPARE = "-diff.png"
- def __init__(self, port, platform, root_output_dir):
+ def __init__(self, port, root_output_dir):
"""Initialize a TestTypeBase object.
Args:
- platform: the platform (e.g., 'chromium-mac-leopard')
- identifying the platform-specific results to be used.
+ port: object implementing port-specific information and methods
root_output_dir: The unix style path to the output dir.
"""
self._root_output_dir = root_output_dir
self._port = port
- self._platform = platform
def _make_output_directory(self, filename):
"""Creates the output directory (if needed) for a given test
@@ -90,7 +91,7 @@ class TestTypeBase(object):
self._port.maybe_make_directory(os.path.split(output_filename)[0])
def _save_baseline_data(self, filename, data, modifier):
- """Saves a new baseline file into the platform directory.
+ """Saves a new baseline file into the port's baseline directory.
The file will be named simply "<test>-expected<modifier>", suitable for
use as the expected results in a later run.
@@ -102,15 +103,16 @@ class TestTypeBase(object):
"""
relative_dir = os.path.dirname(
self._port.relative_test_filename(filename))
- output_dir = os.path.join(
- self._port.chromium_baseline_path(self._platform), relative_dir)
+
+ baseline_path = self._port.baseline_path()
+ output_dir = os.path.join(baseline_path, relative_dir)
output_file = os.path.basename(os.path.splitext(filename)[0] +
self.FILENAME_SUFFIX_EXPECTED + modifier)
self._port.maybe_make_directory(output_dir)
output_path = os.path.join(output_dir, output_file)
- logging.debug('writing new baseline to "%s"' % (output_path))
- open(output_path, "wb").write(data)
+ _log.debug('writing new baseline to "%s"' % (output_path))
+ self._write_into_file_at_path(output_path, data)
def output_filename(self, filename, modifier):
"""Returns a filename inside the output dir that contains modifier.
@@ -130,7 +132,7 @@ class TestTypeBase(object):
self._port.relative_test_filename(filename))
return os.path.splitext(output_filename)[0] + modifier
- def compare_output(self, port, filename, output, test_args, target):
+ def compare_output(self, port, filename, output, test_args, configuration):
"""Method that compares the output from the test with the
expected value.
@@ -141,56 +143,59 @@ class TestTypeBase(object):
output: a string containing the output of the test
test_args: a TestArguments object holding optional additional
arguments
- target: Debug or Release
+ configuration: Debug or Release
Return:
a list of TestFailure objects, empty if the test passes
"""
raise NotImplemented
- def write_output_files(self, port, filename, test_type, file_type,
- output, expected, diff=True, wdiff=False):
+ def _write_into_file_at_path(self, file_path, contents):
+ file = open(file_path, "wb")
+ file.write(contents)
+ file.close()
+
+ def write_output_files(self, port, filename, file_type,
+ output, expected, print_text_diffs=False):
"""Writes the test output, the expected output and optionally the diff
between the two to files in the results directory.
The full output filename of the actual, for example, will be
- <filename><test_type>-actual<file_type>
+ <filename>-actual<file_type>
For instance,
- my_test-simp-actual.txt
+ my_test-actual.txt
Args:
filename: The test filename
- test_type: A string describing the test type, e.g. "simp"
file_type: A string describing the test output file type, e.g. ".txt"
output: A string containing the test output
expected: A string containing the expected test output
- diff: if True, write a file containing the diffs too. This should be
- False for results that are not text
- wdiff: if True, write an HTML file containing word-by-word diffs
+ print_text_diffs: True for text diffs. (FIXME: We should be able to get this from the file type?)
"""
self._make_output_directory(filename)
- actual_filename = self.output_filename(filename,
- test_type + self.FILENAME_SUFFIX_ACTUAL + file_type)
- expected_filename = self.output_filename(filename,
- test_type + self.FILENAME_SUFFIX_EXPECTED + file_type)
+ actual_filename = self.output_filename(filename, self.FILENAME_SUFFIX_ACTUAL + file_type)
+ expected_filename = self.output_filename(filename, self.FILENAME_SUFFIX_EXPECTED + file_type)
if output:
- open(actual_filename, "wb").write(output)
+ self._write_into_file_at_path(actual_filename, output)
if expected:
- open(expected_filename, "wb").write(expected)
+ self._write_into_file_at_path(expected_filename, expected)
if not output or not expected:
return
- if diff:
- diff = port.diff_text(expected, output, expected_filename,
- actual_filename)
- diff_filename = self.output_filename(filename,
- test_type + self.FILENAME_SUFFIX_DIFF + file_type)
- open(diff_filename, "wb").write(diff)
-
- if wdiff:
- # Shell out to wdiff to get colored inline diffs.
- wdiff = port.wdiff_text(expected_filename, actual_filename)
- filename = self.output_filename(filename, test_type +
- self.FILENAME_SUFFIX_WDIFF)
- out = open(filename, 'wb').write(wdiff)
+ if not print_text_diffs:
+ return
+
+ diff = port.diff_text(expected, output, expected_filename, actual_filename)
+ diff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_DIFF + file_type)
+ self._write_into_file_at_path(diff_filename, diff)
+
+ # Shell out to wdiff to get colored inline diffs.
+ wdiff = port.wdiff_text(expected_filename, actual_filename)
+ wdiff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_WDIFF)
+ self._write_into_file_at_path(wdiff_filename, wdiff)
+
+ # Use WebKit's PrettyPatch.rb to get an HTML diff.
+ pretty_patch = port.pretty_patch_text(diff_filename)
+ pretty_patch_filename = self.output_filename(filename, self.FILENAME_SUFFIX_PRETTY_PATCH)
+ self._write_into_file_at_path(pretty_patch_filename, pretty_patch)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py
index 54b332b..8f7907c 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py
@@ -37,8 +37,10 @@ import errno
import logging
import os.path
-from layout_package import test_failures
-from test_types import test_type_base
+from webkitpy.layout_tests.layout_package import test_failures
+from webkitpy.layout_tests.test_types import test_type_base
+
+_log = logging.getLogger("webkitpy.layout_tests.test_types.text_diff")
def is_render_tree_dump(data):
@@ -63,7 +65,7 @@ class TestTextDiff(test_type_base.TestTypeBase):
# Read the port-specific expected text.
expected_filename = self._port.expected_filename(filename, '.txt')
if show_sources:
- logging.debug('Using %s' % expected_filename)
+ _log.debug('Using %s' % expected_filename)
return self.get_normalized_text(expected_filename)
@@ -78,7 +80,7 @@ class TestTextDiff(test_type_base.TestTypeBase):
# Normalize line endings
return text.strip("\r\n").replace("\r\n", "\n") + "\n"
- def compare_output(self, port, filename, output, test_args, target):
+ def compare_output(self, port, filename, output, test_args, configuration):
"""Implementation of CompareOutput that checks the output text against
the expected text from the LayoutTest directory."""
failures = []
@@ -96,8 +98,8 @@ class TestTextDiff(test_type_base.TestTypeBase):
# Write output files for new tests, too.
if port.compare_text(output, expected):
# Text doesn't match, write output files.
- self.write_output_files(port, filename, "", ".txt", output,
- expected, diff=True, wdiff=True)
+ self.write_output_files(port, filename, ".txt", output,
+ expected, print_text_diffs=True)
if expected == '':
failures.append(test_failures.FailureMissingResult(self))
diff --git a/WebKitTools/Scripts/webkitpy/python24/__init__.py b/WebKitTools/Scripts/webkitpy/python24/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/python24/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/python24/versioning.py b/WebKitTools/Scripts/webkitpy/python24/versioning.py
new file mode 100644
index 0000000..8b1f21b
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/python24/versioning.py
@@ -0,0 +1,133 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Supports Python version checking."""
+
+import logging
+import sys
+
+_log = logging.getLogger("webkitpy.python24.versioning")
+
+# The minimum Python version the webkitpy package supports.
+_MINIMUM_SUPPORTED_PYTHON_VERSION = "2.5"
+
+
+def compare_version(sysmodule=None, target_version=None):
+ """Compare the current Python version with a target version.
+
+ Args:
+ sysmodule: An object with version and version_info data attributes
+ used to detect the current Python version. The attributes
+ should have the same semantics as sys.version and
+ sys.version_info. This parameter should only be used
+ for unit testing. Defaults to sys.
+ target_version: A string representing the Python version to compare
+ the current version against. The string should have
+ one of the following three forms: 2, 2.5, or 2.5.3.
+ Defaults to the minimum version that the webkitpy
+ package supports.
+
+ Returns:
+ A triple of (comparison, current_version, target_version).
+
+ comparison: An integer representing the result of comparing the
+ current version with the target version. A positive
+ number means the current version is greater than the
+ target, 0 means they are the same, and a negative number
+ means the current version is less than the target.
+ This method compares version information only up
+ to the precision of the given target version. For
+ example, if the target version is 2.6 and the current
+ version is 2.5.3, this method uses 2.5 for the purposes
+ of comparing with the target.
+ current_version: A string representing the current Python version, for
+ example 2.5.3.
+ target_version: A string representing the version that the current
+ version was compared against, for example 2.5.
+
+ """
+ if sysmodule is None:
+ sysmodule = sys
+ if target_version is None:
+ target_version = _MINIMUM_SUPPORTED_PYTHON_VERSION
+
+ # The number of version parts to compare.
+ precision = len(target_version.split("."))
+
+ # We use sys.version_info rather than sys.version since its first
+ # three elements are guaranteed to be integers.
+ current_version_info_to_compare = sysmodule.version_info[:precision]
+ # Convert integers to strings.
+ current_version_info_to_compare = map(str, current_version_info_to_compare)
+ current_version_to_compare = ".".join(current_version_info_to_compare)
+
+ # Compare version strings lexicographically.
+ if current_version_to_compare > target_version:
+ comparison = 1
+ elif current_version_to_compare == target_version:
+ comparison = 0
+ else:
+ comparison = -1
+
+ # The version number portion of the current version string, for
+ # example "2.6.4".
+ current_version = sysmodule.version.split()[0]
+
+ return (comparison, current_version, target_version)
+
+
+# FIXME: Add a logging level parameter to allow the version message
+# to be logged at levels other than WARNING, for example CRITICAL.
+def check_version(log=None, sysmodule=None, target_version=None):
+ """Check the current Python version against a target version.
+
+ Logs a warning message if the current version is less than the
+ target version.
+
+ Args:
+ log: A logging.logger instance to use when logging the version warning.
+ Defaults to the logger of this module.
+ sysmodule: See the compare_version() docstring.
+ target_version: See the compare_version() docstring.
+
+ Returns:
+ A boolean value of whether the current version is greater than
+ or equal to the target version.
+
+ """
+ if log is None:
+ log = _log
+
+ (comparison, current_version, target_version) = \
+ compare_version(sysmodule, target_version)
+
+ if comparison >= 0:
+ # Then the current version is at least the minimum version.
+ return True
+
+ message = ("WebKit Python scripts do not support your current Python "
+ "version (%s). The minimum supported version is %s.\n"
+ " See the following page to upgrade your Python version:\n\n"
+ " http://trac.webkit.org/wiki/PythonGuidelines\n"
+ % (current_version, target_version))
+ log.warn(message)
+ return False
diff --git a/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py b/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py
new file mode 100644
index 0000000..6939e2d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py
@@ -0,0 +1,134 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Contains unit tests for versioning.py."""
+
+import logging
+import unittest
+
+from webkitpy.common.system.logtesting import LogTesting
+from webkitpy.python24.versioning import check_version
+from webkitpy.python24.versioning import compare_version
+
+class MockSys(object):
+
+ """A mock sys module for passing to version-checking methods."""
+
+ def __init__(self, current_version):
+ """Create an instance.
+
+ current_version: A version string with major, minor, and micro
+ version parts.
+
+ """
+ version_info = current_version.split(".")
+ version_info = map(int, version_info)
+
+ self.version = current_version + " Version details."
+ self.version_info = version_info
+
+
+class CompareVersionTest(unittest.TestCase):
+
+ """Tests compare_version()."""
+
+ def _mock_sys(self, current_version):
+ return MockSys(current_version)
+
+ def test_default_minimum_version(self):
+ """Test the configured minimum version that webkitpy supports."""
+ (comparison, current_version, min_version) = compare_version()
+ self.assertEquals(min_version, "2.5")
+
+ def compare_version(self, target_version, current_version=None):
+ """Call compare_version()."""
+ if current_version is None:
+ current_version = "2.5.3"
+ mock_sys = self._mock_sys(current_version)
+ return compare_version(mock_sys, target_version)
+
+ def compare(self, target_version, current_version=None):
+ """Call compare_version(), and return the comparison."""
+ return self.compare_version(target_version, current_version)[0]
+
+ def test_returned_current_version(self):
+ """Test the current_version return value."""
+ current_version = self.compare_version("2.5")[1]
+ self.assertEquals(current_version, "2.5.3")
+
+ def test_returned_target_version(self):
+ """Test the current_version return value."""
+ target_version = self.compare_version("2.5")[2]
+ self.assertEquals(target_version, "2.5")
+
+ def test_target_version_major(self):
+ """Test major version for target."""
+ self.assertEquals(-1, self.compare("3"))
+ self.assertEquals(0, self.compare("2"))
+ self.assertEquals(1, self.compare("2", "3.0.0"))
+
+ def test_target_version_minor(self):
+ """Test minor version for target."""
+ self.assertEquals(-1, self.compare("2.6"))
+ self.assertEquals(0, self.compare("2.5"))
+ self.assertEquals(1, self.compare("2.4"))
+
+ def test_target_version_micro(self):
+ """Test minor version for target."""
+ self.assertEquals(-1, self.compare("2.5.4"))
+ self.assertEquals(0, self.compare("2.5.3"))
+ self.assertEquals(1, self.compare("2.5.2"))
+
+
+class CheckVersionTest(unittest.TestCase):
+
+ """Tests check_version()."""
+
+ def setUp(self):
+ self._log = LogTesting.setUp(self)
+
+ def tearDown(self):
+ self._log.tearDown()
+
+ def _check_version(self, minimum_version):
+ """Call check_version()."""
+ mock_sys = MockSys("2.5.3")
+ return check_version(sysmodule=mock_sys, target_version=minimum_version)
+
+ def test_true_return_value(self):
+ """Test the configured minimum version that webkitpy supports."""
+ is_current = self._check_version("2.4")
+ self.assertEquals(True, is_current)
+ self._log.assertMessages([]) # No warning was logged.
+
+ def test_false_return_value(self):
+ """Test the configured minimum version that webkitpy supports."""
+ is_current = self._check_version("2.6")
+ self.assertEquals(False, is_current)
+ expected_message = ('WARNING: WebKit Python scripts do not support '
+ 'your current Python version (2.5.3). '
+ 'The minimum supported version is 2.6.\n '
+ 'See the following page to upgrade your Python '
+ 'version:\n\n '
+ 'http://trac.webkit.org/wiki/PythonGuidelines\n\n')
+ self._log.assertMessages([expected_message])
+
diff --git a/WebKitTools/Scripts/webkitpy/steps/__init__.py b/WebKitTools/Scripts/webkitpy/steps/__init__.py
deleted file mode 100644
index 5ae4bea..0000000
--- a/WebKitTools/Scripts/webkitpy/steps/__init__.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2010 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.
-
-# FIXME: Is this the right way to do this?
-from webkitpy.steps.applypatch import ApplyPatch
-from webkitpy.steps.applypatchwithlocalcommit import ApplyPatchWithLocalCommit
-from webkitpy.steps.build import Build
-from webkitpy.steps.checkstyle import CheckStyle
-from webkitpy.steps.cleanworkingdirectory import CleanWorkingDirectory
-from webkitpy.steps.cleanworkingdirectorywithlocalcommits import CleanWorkingDirectoryWithLocalCommits
-from webkitpy.steps.closebug import CloseBug
-from webkitpy.steps.closebugforlanddiff import CloseBugForLandDiff
-from webkitpy.steps.closepatch import ClosePatch
-from webkitpy.steps.commit import Commit
-from webkitpy.steps.completerollout import CompleteRollout
-from webkitpy.steps.confirmdiff import ConfirmDiff
-from webkitpy.steps.createbug import CreateBug
-from webkitpy.steps.editchangelog import EditChangeLog
-from webkitpy.steps.ensurebuildersaregreen import EnsureBuildersAreGreen
-from webkitpy.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded
-from webkitpy.steps.obsoletepatches import ObsoletePatches
-from webkitpy.steps.options import Options
-from webkitpy.steps.postdiff import PostDiff
-from webkitpy.steps.postdiffforcommit import PostDiffForCommit
-from webkitpy.steps.preparechangelogforrevert import PrepareChangeLogForRevert
-from webkitpy.steps.preparechangelog import PrepareChangeLog
-from webkitpy.steps.promptforbugortitle import PromptForBugOrTitle
-from webkitpy.steps.revertrevision import RevertRevision
-from webkitpy.steps.runtests import RunTests
-from webkitpy.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
-from webkitpy.steps.update import Update
diff --git a/WebKitTools/Scripts/webkitpy/steps/completerollout.py b/WebKitTools/Scripts/webkitpy/steps/completerollout.py
deleted file mode 100644
index 8534956..0000000
--- a/WebKitTools/Scripts/webkitpy/steps/completerollout.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (C) 2010 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 webkitpy.comments import bug_comment_from_commit_text
-from webkitpy.steps.build import Build
-from webkitpy.steps.commit import Commit
-from webkitpy.steps.metastep import MetaStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
-
-
-class CompleteRollout(MetaStep):
- substeps = [
- Build,
- Commit,
- ]
-
- @classmethod
- def options(cls):
- collected_options = cls._collect_options_from_steps(cls.substeps)
- collected_options.append(Options.complete_rollout)
- return collected_options
-
- def run(self, state):
- bug_id = state["bug_id"]
- # FIXME: Fully automated rollout is not 100% idiot-proof yet, so for now just log with instructions on how to complete the rollout.
- # Once we trust rollout we will remove this option.
- if not self._options.complete_rollout:
- log("\nNOTE: Rollout support is experimental.\nPlease verify the rollout diff and use \"webkit-patch land %s\" to commit the rollout." % bug_id)
- return
-
- MetaStep.run(self, state)
-
- commit_comment = bug_comment_from_commit_text(self._tool.scm(), state["commit_text"])
- comment_text = "Reverted r%s for reason:\n\n%s\n\n%s" % (state["revision"], state["reason"], commit_comment)
-
- if not bug_id:
- log(comment_text)
- log("No bugs were updated.")
- return
- self._tool.bugs.reopen_bug(bug_id, comment_text)
diff --git a/WebKitTools/Scripts/webkitpy/style/checker.py b/WebKitTools/Scripts/webkitpy/style/checker.py
index 9beda9e..84ae3da 100644
--- a/WebKitTools/Scripts/webkitpy/style/checker.py
+++ b/WebKitTools/Scripts/webkitpy/style/checker.py
@@ -1,5 +1,6 @@
# Copyright (C) 2009 Google Inc. All rights reserved.
# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
+# Copyright (C) 2010 ProFUSION embedded systems
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -30,23 +31,26 @@
"""Front end of some style-checker modules."""
import codecs
+import logging
import os.path
import sys
-from .. style_references import parse_patch
from error_handlers import DefaultStyleErrorHandler
-from error_handlers import PatchStyleErrorHandler
from filter import FilterConfiguration
from optparser import ArgumentParser
from optparser import DefaultCommandOptionValues
-from processors.common import check_no_carriage_return
from processors.common import categories as CommonCategories
+from processors.common import CarriageReturnProcessor
from processors.cpp import CppProcessor
+from processors.python import PythonProcessor
from processors.text import TextProcessor
+from webkitpy.style_references import parse_patch
+from webkitpy.style_references import configure_logging as _configure_logging
+_log = logging.getLogger("webkitpy.style.checker")
# These are default option values for the command-line option parser.
-_DEFAULT_VERBOSITY = 1
+_DEFAULT_MIN_CONFIDENCE = 1
_DEFAULT_OUTPUT_FORMAT = 'emacs'
@@ -85,6 +89,16 @@ _BASE_FILTER_RULES = [
'-whitespace/blank_line',
'-whitespace/end_of_line',
'-whitespace/labels',
+ # List Python pep8 categories last.
+ #
+ # Because much of WebKit's Python code base does not abide by the
+ # PEP8 79 character limit, we ignore the 79-character-limit category
+ # pep8/E501 for now.
+ #
+ # FIXME: Consider bringing WebKit's Python code base into conformance
+ # with the 79 character limit, or some higher limit that is
+ # agreeable to the WebKit project.
+ '-pep8/E501',
]
@@ -106,9 +120,10 @@ _PATH_RULES_SPECIFIER = [
"WebKit/qt/QGVLauncher/"],
["-build/include",
"-readability/streams"]),
- ([# The GTK+ APIs use GTK+ naming style, which includes
- # lower-cased, underscore-separated values.
- "WebKit/gtk/webkit/",
+ ([# The EFL APIs use EFL naming style, which includes
+ # both lower-cased and camel-cased, underscore-sparated
+ # values.
+ "WebKit/efl/ewk/",
# There is no clean way to avoid "yy_*" names used by flex.
"WebCore/css/CSSParser.cpp",
# There is no clean way to avoid "xxx_data" methods inside
@@ -117,6 +132,29 @@ _PATH_RULES_SPECIFIER = [
"WebKit/qt/tests/",
"JavaScriptCore/qt/tests"],
["-readability/naming"]),
+ ([# The GTK+ APIs use GTK+ naming style, which includes
+ # lower-cased, underscore-separated values.
+ # Also, GTK+ allows the use of NULL.
+ "WebKit/gtk/webkit/",
+ "WebKitTools/DumpRenderTree/gtk/"],
+ ["-readability/naming",
+ "-readability/null"]),
+ ([# Header files in ForwardingHeaders have no header guards or
+ # exceptional header guards (e.g., WebCore_FWD_Debugger_h).
+ "/ForwardingHeaders/"],
+ ["-build/header_guard"]),
+
+ # For third-party Python code, keep only the following checks--
+ #
+ # No tabs: to avoid having to set the SVN allow-tabs property.
+ # No trailing white space: since this is easy to correct.
+ # No carriage-return line endings: since this is easy to correct.
+ #
+ (["webkitpy/thirdparty/"],
+ ["-",
+ "+pep8/W191", # Tabs
+ "+pep8/W291", # Trailing white space
+ "+whitespace/carriage_return"]),
]
@@ -140,7 +178,8 @@ _SKIPPED_FILES_WITH_WARNING = [
# Don't include a warning for skipped files that are more common
# and more obvious.
_SKIPPED_FILES_WITHOUT_WARNING = [
- "LayoutTests/"
+ "LayoutTests/",
+ ".pyc",
]
@@ -154,13 +193,22 @@ _MAX_REPORTS_PER_CATEGORY = {
def _all_categories():
"""Return the set of all categories used by check-webkit-style."""
# Take the union across all processors.
- return CommonCategories.union(CppProcessor.categories)
+ categories = CommonCategories.union(CppProcessor.categories)
+
+ # FIXME: Consider adding all of the pep8 categories. Since they
+ # are not too meaningful for documentation purposes, for
+ # now we add only the categories needed for the unit tests
+ # (which validate the consistency of the configuration
+ # settings against the known categories, etc).
+ categories = categories.union(["pep8/W191", "pep8/W291", "pep8/E501"])
+
+ return categories
def _check_webkit_style_defaults():
"""Return the default command-line options for check-webkit-style."""
- return DefaultCommandOptionValues(output_format=_DEFAULT_OUTPUT_FORMAT,
- verbosity=_DEFAULT_VERBOSITY)
+ return DefaultCommandOptionValues(min_confidence=_DEFAULT_MIN_CONFIDENCE,
+ output_format=_DEFAULT_OUTPUT_FORMAT)
# This function assists in optparser not having to import from checker.
@@ -186,9 +234,97 @@ def check_webkit_style_configuration(options):
return StyleCheckerConfiguration(filter_configuration=filter_configuration,
max_reports_per_category=_MAX_REPORTS_PER_CATEGORY,
+ min_confidence=options.min_confidence,
output_format=options.output_format,
- stderr_write=sys.stderr.write,
- verbosity=options.verbosity)
+ stderr_write=sys.stderr.write)
+
+
+def _create_log_handlers(stream):
+ """Create and return a default list of logging.Handler instances.
+
+ Format WARNING messages and above to display the logging level, and
+ messages strictly below WARNING not to display it.
+
+ Args:
+ stream: See the configure_logging() docstring.
+
+ """
+ # Handles logging.WARNING and above.
+ error_handler = logging.StreamHandler(stream)
+ error_handler.setLevel(logging.WARNING)
+ formatter = logging.Formatter("%(levelname)s: %(message)s")
+ error_handler.setFormatter(formatter)
+
+ # Create a logging.Filter instance that only accepts messages
+ # below WARNING (i.e. filters out anything WARNING or above).
+ non_error_filter = logging.Filter()
+ # The filter method accepts a logging.LogRecord instance.
+ non_error_filter.filter = lambda record: record.levelno < logging.WARNING
+
+ non_error_handler = logging.StreamHandler(stream)
+ non_error_handler.addFilter(non_error_filter)
+ formatter = logging.Formatter("%(message)s")
+ non_error_handler.setFormatter(formatter)
+
+ return [error_handler, non_error_handler]
+
+
+def _create_debug_log_handlers(stream):
+ """Create and return a list of logging.Handler instances for debugging.
+
+ Args:
+ stream: See the configure_logging() docstring.
+
+ """
+ handler = logging.StreamHandler(stream)
+ formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s")
+ handler.setFormatter(formatter)
+
+ return [handler]
+
+
+def configure_logging(stream, logger=None, is_verbose=False):
+ """Configure logging, and return the list of handlers added.
+
+ Returns:
+ A list of references to the logging handlers added to the root
+ logger. This allows the caller to later remove the handlers
+ using logger.removeHandler. This is useful primarily during unit
+ testing where the caller may want to configure logging temporarily
+ and then undo the configuring.
+
+ Args:
+ stream: A file-like object to which to log. The stream must
+ define an "encoding" data attribute, or else logging
+ raises an error.
+ logger: A logging.logger instance to configure. This parameter
+ should be used only in unit tests. Defaults to the
+ root logger.
+ is_verbose: A boolean value of whether logging should be verbose.
+
+ """
+ # If the stream does not define an "encoding" data attribute, the
+ # logging module can throw an error like the following:
+ #
+ # Traceback (most recent call last):
+ # File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
+ # lib/python2.6/logging/__init__.py", line 761, in emit
+ # self.stream.write(fs % msg.encode(self.stream.encoding))
+ # LookupError: unknown encoding: unknown
+ if logger is None:
+ logger = logging.getLogger()
+
+ if is_verbose:
+ logging_level = logging.DEBUG
+ handlers = _create_debug_log_handlers(stream)
+ else:
+ logging_level = logging.INFO
+ handlers = _create_log_handlers(stream)
+
+ handlers = _configure_logging(logging_level=logging_level, logger=logger,
+ handlers=handlers)
+
+ return handlers
# Enum-like idiom
@@ -197,7 +333,8 @@ class FileType:
NONE = 1
# Alphabetize remaining types
CPP = 2
- TEXT = 3
+ PYTHON = 3
+ TEXT = 4
class ProcessorDispatcher(object):
@@ -218,7 +355,6 @@ class ProcessorDispatcher(object):
'mm',
'php',
'pm',
- 'py',
'txt',
)
@@ -252,20 +388,26 @@ class ProcessorDispatcher(object):
# reading from stdin, cpp_style tests should not rely on
# the extension.
return FileType.CPP
- elif ("ChangeLog" in file_path
- or "WebKitTools/Scripts/" in file_path
- or file_extension in self.text_file_extensions):
+ elif file_extension == "py":
+ return FileType.PYTHON
+ elif ("ChangeLog" in file_path or
+ (not file_extension and "WebKitTools/Scripts/" in file_path) or
+ file_extension in self.text_file_extensions):
return FileType.TEXT
else:
return FileType.NONE
- def _create_processor(self, file_type, file_path, handle_style_error, verbosity):
+ def _create_processor(self, file_type, file_path, handle_style_error,
+ min_confidence):
"""Instantiate and return a style processor based on file type."""
if file_type == FileType.NONE:
processor = None
elif file_type == FileType.CPP:
file_extension = self._file_extension(file_path)
- processor = CppProcessor(file_path, file_extension, handle_style_error, verbosity)
+ processor = CppProcessor(file_path, file_extension,
+ handle_style_error, min_confidence)
+ elif file_type == FileType.PYTHON:
+ processor = PythonProcessor(file_path, handle_style_error)
elif file_type == FileType.TEXT:
processor = TextProcessor(file_path, handle_style_error)
else:
@@ -278,39 +420,41 @@ class ProcessorDispatcher(object):
return processor
- def dispatch_processor(self, file_path, handle_style_error, verbosity):
+ def dispatch_processor(self, file_path, handle_style_error, min_confidence):
"""Instantiate and return a style processor based on file path."""
file_type = self._file_type(file_path)
processor = self._create_processor(file_type,
file_path,
handle_style_error,
- verbosity)
+ min_confidence)
return processor
+# FIXME: Remove the stderr_write attribute from this class and replace
+# its use with calls to a logging module logger.
class StyleCheckerConfiguration(object):
"""Stores configuration values for the StyleChecker class.
Attributes:
+ min_confidence: An integer between 1 and 5 inclusive that is the
+ minimum confidence level of style errors to report.
+
max_reports_per_category: The maximum number of errors to report
per category, per file.
stderr_write: A function that takes a string as a parameter and
serves as stderr.write.
- verbosity: An integer between 1-5 inclusive that restricts output
- to errors with a confidence score at or above this value.
-
"""
def __init__(self,
filter_configuration,
max_reports_per_category,
+ min_confidence,
output_format,
- stderr_write,
- verbosity):
+ stderr_write):
"""Create a StyleCheckerConfiguration instance.
Args:
@@ -321,6 +465,10 @@ class StyleCheckerConfiguration(object):
max_reports_per_category: The maximum number of errors to report
per category, per file.
+ min_confidence: An integer between 1 and 5 inclusive that is the
+ minimum confidence level of style errors to report.
+ The default is 1, which reports all style errors.
+
output_format: A string that is the output format. The supported
output formats are "emacs" which emacs can parse
and "vs7" which Microsoft Visual Studio 7 can parse.
@@ -328,42 +476,37 @@ class StyleCheckerConfiguration(object):
stderr_write: A function that takes a string as a parameter and
serves as stderr.write.
- verbosity: An integer between 1-5 inclusive that restricts output
- to errors with a confidence score at or above this value.
- The default is 1, which reports all errors.
-
"""
self._filter_configuration = filter_configuration
self._output_format = output_format
self.max_reports_per_category = max_reports_per_category
+ self.min_confidence = min_confidence
self.stderr_write = stderr_write
- self.verbosity = verbosity
def is_reportable(self, category, confidence_in_error, file_path):
"""Return whether an error is reportable.
An error is reportable if both the confidence in the error is
- at least the current verbosity level and the current filter
+ at least the minimum confidence level and the current filter
says the category should be checked for the given path.
Args:
category: A string that is a style category.
- confidence_in_error: An integer between 1 and 5, inclusive, that
- represents the application's confidence in
- the error. A higher number signifies greater
- confidence.
+ confidence_in_error: An integer between 1 and 5 inclusive that is
+ the application's confidence in the error.
+ A higher number means greater confidence.
file_path: The path of the file being checked
"""
- if confidence_in_error < self.verbosity:
+ if confidence_in_error < self.min_confidence:
return False
return self._filter_configuration.should_check(category, file_path)
def write_style_error(self,
category,
- confidence,
+ confidence_in_error,
file_path,
line_number,
message):
@@ -377,9 +520,38 @@ class StyleCheckerConfiguration(object):
line_number,
message,
category,
- confidence))
+ confidence_in_error))
+class ProcessorBase(object):
+
+ """The base class for processors of lists of lines."""
+
+ def should_process(self, file_path):
+ """Return whether the file at file_path should be processed."""
+ raise NotImplementedError('Subclasses should implement.')
+
+ def process(self, lines, file_path, **kwargs):
+ """Process lines of text read from a file.
+
+ Args:
+ lines: A list of lines of text to process.
+ file_path: The path from which the lines were read.
+ **kwargs: This argument signifies that the process() method of
+ subclasses of ProcessorBase may support additional
+ keyword arguments.
+ For example, a style processor's process() method
+ may support a "reportable_lines" parameter that represents
+ the line numbers of the lines for which style errors
+ should be reported.
+
+ """
+ raise NotImplementedError('Subclasses should implement.')
+
+
+# FIXME: Modify this class to start using the TextFileReader class in
+# webkitpy/style/filereader.py. This probably means creating
+# a StyleProcessor class that inherits from ProcessorBase.
class StyleChecker(object):
"""Supports checking style in files and patches.
@@ -406,79 +578,142 @@ class StyleChecker(object):
self.error_count = 0
self.file_count = 0
- def _stderr_write(self, message):
- self._configuration.stderr_write(message)
-
def _increment_error_count(self):
"""Increment the total count of reported errors."""
self.error_count += 1
- def _process_file(self, processor, file_path, handle_style_error):
- """Process the file using the given processor."""
- try:
- # Support the UNIX convention of using "-" for stdin. Note that
- # we are not opening the file with universal newline support
- # (which codecs doesn't support anyway), so the resulting lines do
- # contain trailing '\r' characters if we are reading a file that
- # has CRLF endings.
- # If after the split a trailing '\r' is present, it is removed
- # below. If it is not expected to be present (i.e. os.linesep !=
- # '\r\n' as in Windows), a warning is issued below if this file
- # is processed.
- if file_path == '-':
- file = codecs.StreamReaderWriter(sys.stdin,
- codecs.getreader('utf8'),
- codecs.getwriter('utf8'),
- 'replace')
- else:
- file = codecs.open(file_path, 'r', 'utf8', 'replace')
+ def _read_lines(self, file_path):
+ """Read the file at a path, and return its lines.
- contents = file.read()
+ Raises:
+ IOError: if the file does not exist or cannot be read.
- except IOError:
- self._stderr_write("Skipping input '%s': Can't open for reading\n" % file_path)
- return
+ """
+ # Support the UNIX convention of using "-" for stdin.
+ if file_path == '-':
+ file = codecs.StreamReaderWriter(sys.stdin,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+ else:
+ # We do not open the file with universal newline support
+ # (codecs does not support it anyway), so the resulting
+ # lines contain trailing "\r" characters if we are reading
+ # a file with CRLF endings.
+ file = codecs.open(file_path, 'r', 'utf8', 'replace')
+
+ contents = file.read()
lines = contents.split("\n")
+ return lines
- for line_number in range(len(lines)):
- # FIXME: We should probably use the SVN "eol-style" property
- # or a white list to decide whether or not to do
- # the carriage-return check. Originally, we did the
- # check only if (os.linesep != '\r\n').
- #
- # FIXME: As a minor optimization, we can have
- # check_no_carriage_return() return whether
- # the line ends with "\r".
- check_no_carriage_return(lines[line_number], line_number,
- handle_style_error)
- if lines[line_number].endswith("\r"):
- lines[line_number] = lines[line_number].rstrip("\r")
+ def _process_file(self, processor, file_path, handle_style_error):
+ """Process the file using the given style processor."""
+ try:
+ lines = self._read_lines(file_path)
+ except IOError:
+ message = 'Could not read file. Skipping: "%s"' % file_path
+ _log.warn(message)
+ return
+
+ # Check for and remove trailing carriage returns ("\r").
+ #
+ # FIXME: We should probably use the SVN "eol-style" property
+ # or a white list to decide whether or not to do
+ # the carriage-return check. Originally, we did the
+ # check only if (os.linesep != '\r\n').
+ carriage_return_processor = CarriageReturnProcessor(handle_style_error)
+ lines = carriage_return_processor.process(lines)
processor.process(lines)
- def check_file(self, file_path, handle_style_error=None, process_file=None):
+ def check_paths(self, paths, mock_check_file=None, mock_os=None):
+ """Check style in the given files or directories.
+
+ Args:
+ paths: A list of file paths and directory paths.
+ mock_check_file: A mock of self.check_file for unit testing.
+ mock_os: A mock os for unit testing.
+
+ """
+ check_file = self.check_file if mock_check_file is None else \
+ mock_check_file
+ os_module = os if mock_os is None else mock_os
+
+ for path in paths:
+ if os_module.path.isdir(path):
+ self._check_directory(directory=path,
+ check_file=check_file,
+ mock_os_walk=os_module.walk)
+ else:
+ check_file(path)
+
+ def _check_directory(self, directory, check_file, mock_os_walk=None):
+ """Check style in all files in a directory, recursively.
+
+ Args:
+ directory: A path to a directory.
+ check_file: The function to use in place of self.check_file().
+ mock_os_walk: A mock os.walk for unit testing.
+
+ """
+ os_walk = os.walk if mock_os_walk is None else mock_os_walk
+
+ for dir_path, dir_names, file_names in os_walk(directory):
+ for file_name in file_names:
+ file_path = os.path.join(dir_path, file_name)
+ check_file(file_path)
+
+ def check_file(self, file_path, line_numbers=None,
+ mock_handle_style_error=None,
+ mock_os_path_exists=None,
+ mock_process_file=None):
"""Check style in the given file.
Args:
- file_path: A string that is the path of the file to process.
- handle_style_error: The function to call when a style error
- occurs. This parameter is meant for internal
- use within this class. Defaults to a
- DefaultStyleErrorHandler instance.
- process_file: The function to call to process the file. This
- parameter should be used only for unit tests.
- Defaults to the file processing method of this class.
+ file_path: The path of the file to process. If possible, the path
+ should be relative to the source root. Otherwise,
+ path-specific logic may not behave as expected.
+ line_numbers: An array of line numbers of the lines for which
+ style errors should be reported, or None if errors
+ for all lines should be reported. Normally, this
+ array contains the line numbers corresponding to the
+ modified lines of a patch.
+ mock_handle_style_error: A unit-testing replacement for the function
+ to call when a style error occurs. Defaults
+ to a DefaultStyleErrorHandler instance.
+ mock_os_path_exists: A unit-test replacement for os.path.exists.
+ This parameter should only be used for unit
+ tests.
+ mock_process_file: The function to call to process the file. This
+ parameter should be used only for unit tests.
+ Defaults to the file processing method of this
+ class.
+
+ Raises:
+ SystemExit: if the file does not exist.
"""
- if handle_style_error is None:
+ if mock_handle_style_error is None:
+ increment = self._increment_error_count
handle_style_error = DefaultStyleErrorHandler(
configuration=self._configuration,
file_path=file_path,
- increment_error_count=
- self._increment_error_count)
- if process_file is None:
- process_file = self._process_file
+ increment_error_count=increment,
+ line_numbers=line_numbers)
+ else:
+ handle_style_error = mock_handle_style_error
+
+ os_path_exists = (os.path.exists if mock_os_path_exists is None else
+ mock_os_path_exists)
+ process_file = (self._process_file if mock_process_file is None else
+ mock_process_file)
+
+ if not os_path_exists(file_path) and file_path != "-":
+ _log.error("File does not exist: %s" % file_path)
+ sys.exit(1)
+
+ _log.debug("Checking: " + file_path)
self.file_count += 1
@@ -487,31 +722,58 @@ class StyleChecker(object):
if dispatcher.should_skip_without_warning(file_path):
return
if dispatcher.should_skip_with_warning(file_path):
- self._stderr_write('Ignoring "%s": this file is exempt from the '
- "style guide.\n" % file_path)
+ _log.warn('File exempt from style guide. Skipping: "%s"'
+ % file_path)
return
- verbosity = self._configuration.verbosity
+ min_confidence = self._configuration.min_confidence
processor = dispatcher.dispatch_processor(file_path,
handle_style_error,
- verbosity)
+ min_confidence)
if processor is None:
+ _log.debug('File not a recognized type to check. Skipping: "%s"'
+ % file_path)
return
+ _log.debug("Using class: " + processor.__class__.__name__)
+
process_file(processor, file_path, handle_style_error)
- def check_patch(self, patch_string):
- """Check style in the given patch.
+
+class PatchChecker(object):
+
+ """Supports checking style in patches."""
+
+ def __init__(self, style_checker):
+ """Create a PatchChecker instance.
Args:
- patch_string: A string that is a patch string.
+ style_checker: A StyleChecker instance.
"""
+ self._file_checker = style_checker
+
+ def check(self, patch_string):
+ """Check style in the given patch."""
patch_files = parse_patch(patch_string)
- for file_path, diff in patch_files.iteritems():
- style_error_handler = PatchStyleErrorHandler(diff,
- file_path,
- self._configuration,
- self._increment_error_count)
- self.check_file(file_path, style_error_handler)
+ # The diff variable is a DiffFile instance.
+ for path, diff in patch_files.iteritems():
+ line_numbers = set()
+ for line in diff.lines:
+ # When deleted line is not set, it means that
+ # the line is newly added (or modified).
+ if not line[0]:
+ line_numbers.add(line[1])
+
+ _log.debug('Found %s new or modified lines in: %s'
+ % (len(line_numbers), path))
+
+ # If line_numbers is empty, the file has no new or
+ # modified lines. In this case, we don't check the file
+ # because we'll never output errors for the file.
+ # This optimization also prevents the program from exiting
+ # due to a deleted file.
+ if line_numbers:
+ self._file_checker.check_file(file_path=path,
+ line_numbers=line_numbers)
diff --git a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
index fe12512..401a7b3 100755
--- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
@@ -34,16 +34,23 @@
"""Unit tests for style.py."""
+import logging
+import os
import unittest
import checker as style
+from webkitpy.style_references import parse_patch
+from webkitpy.style_references import LogTesting
+from webkitpy.style_references import TestLogStream
from checker import _BASE_FILTER_RULES
from checker import _MAX_REPORTS_PER_CATEGORY
from checker import _PATH_RULES_SPECIFIER as PATH_RULES_SPECIFIER
from checker import _all_categories
from checker import check_webkit_style_configuration
from checker import check_webkit_style_parser
+from checker import configure_logging
from checker import ProcessorDispatcher
+from checker import PatchChecker
from checker import StyleChecker
from checker import StyleCheckerConfiguration
from filter import validate_filter_rules
@@ -51,7 +58,94 @@ from filter import FilterConfiguration
from optparser import ArgumentParser
from optparser import CommandOptionValues
from processors.cpp import CppProcessor
+from processors.python import PythonProcessor
from processors.text import TextProcessor
+from webkitpy.common.system.logtesting import LoggingTestCase
+
+
+class ConfigureLoggingTestBase(unittest.TestCase):
+
+ """Base class for testing configure_logging().
+
+ Sub-classes should implement:
+
+ is_verbose: The is_verbose value to pass to configure_logging().
+
+ """
+
+ def setUp(self):
+ is_verbose = self.is_verbose
+
+ log_stream = TestLogStream(self)
+
+ # Use a logger other than the root logger or one prefixed with
+ # webkit so as not to conflict with test-webkitpy logging.
+ logger = logging.getLogger("unittest")
+
+ # Configure the test logger not to pass messages along to the
+ # root logger. This prevents test messages from being
+ # propagated to loggers used by test-webkitpy logging (e.g.
+ # the root logger).
+ logger.propagate = False
+
+ self._handlers = configure_logging(stream=log_stream, logger=logger,
+ is_verbose=is_verbose)
+ self._log = logger
+ self._log_stream = log_stream
+
+ def tearDown(self):
+ """Reset logging to its original state.
+
+ This method ensures that the logging configuration set up
+ for a unit test does not affect logging in other unit tests.
+
+ """
+ logger = self._log
+ for handler in self._handlers:
+ logger.removeHandler(handler)
+
+ def assert_log_messages(self, messages):
+ """Assert that the logged messages equal the given messages."""
+ self._log_stream.assertMessages(messages)
+
+
+class ConfigureLoggingTest(ConfigureLoggingTestBase):
+
+ """Tests the configure_logging() function."""
+
+ is_verbose = False
+
+ def test_warning_message(self):
+ self._log.warn("test message")
+ self.assert_log_messages(["WARNING: test message\n"])
+
+ def test_below_warning_message(self):
+ # We test the boundary case of a logging level equal to 29.
+ # In practice, we will probably only be calling log.info(),
+ # which corresponds to a logging level of 20.
+ level = logging.WARNING - 1 # Equals 29.
+ self._log.log(level, "test message")
+ self.assert_log_messages(["test message\n"])
+
+ def test_debug_message(self):
+ self._log.debug("test message")
+ self.assert_log_messages([])
+
+ def test_two_messages(self):
+ self._log.info("message1")
+ self._log.info("message2")
+ self.assert_log_messages(["message1\n", "message2\n"])
+
+
+class ConfigureLoggingVerboseTest(ConfigureLoggingTestBase):
+
+ """Tests the configure_logging() function with is_verbose True."""
+
+ is_verbose = True
+
+ def test_debug_message(self):
+ self._log.debug("test message")
+ self.assert_log_messages(["unittest: DEBUG test message\n"])
class GlobalVariablesTest(unittest.TestCase):
@@ -91,7 +185,9 @@ class GlobalVariablesTest(unittest.TestCase):
default_options=default_options)
# No need to test the return value here since we test parse()
# on valid arguments elsewhere.
- parser.parse([]) # arguments valid: no error or SystemExit
+ #
+ # The default options are valid: no error or SystemExit.
+ parser.parse(args=[])
def test_path_rules_specifier(self):
all_categories = self._all_categories()
@@ -125,6 +221,10 @@ class GlobalVariablesTest(unittest.TestCase):
"readability/naming")
assertNoCheck("WebKit/gtk/webkit/webkit.h",
"readability/naming")
+ assertNoCheck("WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp",
+ "readability/null")
+ assertNoCheck("WebKit/efl/ewk/ewk_view.h",
+ "readability/naming")
assertNoCheck("WebCore/css/CSSParser.cpp",
"readability/naming")
assertNoCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp",
@@ -132,6 +232,16 @@ class GlobalVariablesTest(unittest.TestCase):
assertNoCheck(
"JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp",
"readability/naming")
+ assertNoCheck("WebCore/ForwardingHeaders/debugger/Debugger.h",
+ "build/header_guard")
+
+ # Third-party Python code: webkitpy/thirdparty
+ path = "WebKitTools/Scripts/webkitpy/thirdparty/mock.py"
+ assertNoCheck(path, "build/include")
+ assertNoCheck(path, "pep8/E401") # A random pep8 category.
+ assertCheck(path, "pep8/W191")
+ assertCheck(path, "pep8/W291")
+ assertCheck(path, "whitespace/carriage_return")
def test_max_reports_per_category(self):
"""Check that _MAX_REPORTS_PER_CATEGORY is valid."""
@@ -212,7 +322,7 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
dispatcher = ProcessorDispatcher()
processor = dispatcher.dispatch_processor(file_path,
self.mock_handle_style_error,
- verbosity=3)
+ min_confidence=3)
return processor
def assert_processor_none(self, file_path):
@@ -235,6 +345,10 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
"""Assert that the dispatched processor is a CppProcessor."""
self.assert_processor(file_path, CppProcessor)
+ def assert_processor_python(self, file_path):
+ """Assert that the dispatched processor is a PythonProcessor."""
+ self.assert_processor(file_path, PythonProcessor)
+
def assert_processor_text(self, file_path):
"""Assert that the dispatched processor is a TextProcessor."""
self.assert_processor(file_path, TextProcessor)
@@ -260,7 +374,7 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
self.assertEquals(processor.file_extension, file_extension)
self.assertEquals(processor.file_path, file_path)
self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
- self.assertEquals(processor.verbosity, 3)
+ self.assertEquals(processor.min_confidence, 3)
# Check "-" for good measure.
file_base = "-"
file_extension = ""
@@ -270,6 +384,26 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
self.assertEquals(processor.file_extension, file_extension)
self.assertEquals(processor.file_path, file_path)
+ def test_python_paths(self):
+ """Test paths that should be checked as Python."""
+ paths = [
+ "foo.py",
+ "WebKitTools/Scripts/modules/text_style.py",
+ ]
+
+ for path in paths:
+ self.assert_processor_python(path)
+
+ # Check processor attributes on a typical input.
+ file_base = "foo"
+ file_extension = "css"
+ file_path = file_base + "." + file_extension
+ self.assert_processor_text(file_path)
+ processor = self.dispatch_processor(file_path)
+ self.assertEquals(processor.file_path, file_path)
+ self.assertEquals(processor.handle_style_error,
+ self.mock_handle_style_error)
+
def test_text_paths(self):
"""Test paths that should be checked as text."""
paths = [
@@ -281,14 +415,12 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
"foo.mm",
"foo.php",
"foo.pm",
- "foo.py",
"foo.txt",
"FooChangeLog.bak",
"WebCore/ChangeLog",
"WebCore/inspector/front-end/inspector.js",
- "WebKitTools/Scripts/check-webkit=style",
- "WebKitTools/Scripts/modules/text_style.py",
- ]
+ "WebKitTools/Scripts/check-webkit-style",
+ ]
for path in paths:
self.assert_processor_text(path)
@@ -333,9 +465,9 @@ class StyleCheckerConfigurationTest(unittest.TestCase):
return StyleCheckerConfiguration(
filter_configuration=filter_configuration,
max_reports_per_category={"whitespace/newline": 1},
+ min_confidence=3,
output_format=output_format,
- stderr_write=self._mock_stderr_write,
- verbosity=3)
+ stderr_write=self._mock_stderr_write)
def test_init(self):
"""Test the __init__() method."""
@@ -345,7 +477,7 @@ class StyleCheckerConfigurationTest(unittest.TestCase):
self.assertEquals(configuration.max_reports_per_category,
{"whitespace/newline": 1})
self.assertEquals(configuration.stderr_write, self._mock_stderr_write)
- self.assertEquals(configuration.verbosity, 3)
+ self.assertEquals(configuration.min_confidence, 3)
def test_is_reportable(self):
"""Test the is_reportable() method."""
@@ -362,7 +494,7 @@ class StyleCheckerConfigurationTest(unittest.TestCase):
def _call_write_style_error(self, output_format):
config = self._style_checker_configuration(output_format=output_format)
config.write_style_error(category="whitespace/tab",
- confidence=5,
+ confidence_in_error=5,
file_path="foo.h",
line_number=100,
message="message")
@@ -395,9 +527,9 @@ class StyleCheckerTest(unittest.TestCase):
configuration = StyleCheckerConfiguration(
filter_configuration=FilterConfiguration(),
max_reports_per_category={},
+ min_confidence=3,
output_format="vs7",
- stderr_write=self._mock_stderr_write,
- verbosity=3)
+ stderr_write=self._mock_stderr_write)
style_checker = self._style_checker(configuration)
@@ -406,7 +538,25 @@ class StyleCheckerTest(unittest.TestCase):
self.assertEquals(style_checker.file_count, 0)
-class StyleCheckerCheckFileTest(unittest.TestCase):
+class StyleCheckerCheckFileBase(LoggingTestCase):
+
+ def setUp(self):
+ LoggingTestCase.setUp(self)
+ self.warning_messages = ""
+
+ def mock_stderr_write(self, warning_message):
+ self.warning_messages += warning_message
+
+ def _style_checker_configuration(self):
+ return StyleCheckerConfiguration(
+ filter_configuration=FilterConfiguration(),
+ max_reports_per_category={"whitespace/newline": 1},
+ min_confidence=3,
+ output_format="vs7",
+ stderr_write=self.mock_stderr_write)
+
+
+class StyleCheckerCheckFileTest(StyleCheckerCheckFileBase):
"""Test the check_file() method of the StyleChecker class.
@@ -432,17 +582,19 @@ class StyleCheckerCheckFileTest(unittest.TestCase):
"""
def setUp(self):
+ StyleCheckerCheckFileBase.setUp(self)
self.got_file_path = None
self.got_handle_style_error = None
self.got_processor = None
- self.warning_messages = ""
-
- def mock_stderr_write(self, warning_message):
- self.warning_messages += warning_message
def mock_handle_style_error(self):
pass
+ def mock_os_path_exists(self, path):
+ # We deliberately make it so that this method returns False unless
+ # the caller has made an effort to put "does_exist" in the path.
+ return path.find("does_exist") > -1
+
def mock_process_file(self, processor, file_path, handle_style_error):
"""A mock _process_file().
@@ -470,25 +622,48 @@ class StyleCheckerCheckFileTest(unittest.TestCase):
# Confirm that the attributes are reset.
self.assert_attributes(None, None, None, "")
- configuration = StyleCheckerConfiguration(
- filter_configuration=FilterConfiguration(),
- max_reports_per_category={"whitespace/newline": 1},
- output_format="vs7",
- stderr_write=self.mock_stderr_write,
- verbosity=3)
+ configuration = self._style_checker_configuration()
style_checker = StyleChecker(configuration)
- style_checker.check_file(file_path,
- self.mock_handle_style_error,
- self.mock_process_file)
+ style_checker.check_file(file_path=file_path,
+ mock_handle_style_error=self.mock_handle_style_error,
+ mock_os_path_exists=self.mock_os_path_exists,
+ mock_process_file=self.mock_process_file)
+
+ self.assertEquals(style_checker.file_count, 1)
+
+ def test_check_file_does_not_exist(self):
+ file_path = "file_does_not_exist.txt"
- self.assertEquals(1, style_checker.file_count)
+ # Confirm that the file does not exist.
+ self.assertFalse(self.mock_os_path_exists(file_path))
+
+ # Check the outcome.
+ self.assertRaises(SystemExit, self.call_check_file, file_path)
+ self.assertLog(["ERROR: File does not exist: "
+ "file_does_not_exist.txt\n"])
+
+ def test_check_file_stdin(self):
+ file_path = "-"
+
+ # Confirm that the file does not exist.
+ self.assertFalse(self.mock_os_path_exists(file_path))
+
+ # Check the outcome.
+ self.call_check_file(file_path)
+ expected_processor = CppProcessor(file_path,
+ "",
+ self.mock_handle_style_error, 3)
+ self.assert_attributes(file_path,
+ self.mock_handle_style_error,
+ expected_processor,
+ "")
def test_check_file_on_skip_without_warning(self):
"""Test check_file() for a skipped-without-warning file."""
- file_path = "LayoutTests/foo.txt"
+ file_path = "LayoutTests/does_exist/foo.txt"
dispatcher = ProcessorDispatcher()
# Confirm that the input file is truly a skipped-without-warning file.
@@ -501,7 +676,7 @@ class StyleCheckerCheckFileTest(unittest.TestCase):
def test_check_file_on_skip_with_warning(self):
"""Test check_file() for a skipped-with-warning file."""
- file_path = "gtk2drawing.c"
+ file_path = "does_exist/gtk2drawing.c"
dispatcher = ProcessorDispatcher()
# Check that the input file is truly a skipped-with-warning file.
@@ -509,15 +684,16 @@ class StyleCheckerCheckFileTest(unittest.TestCase):
# Check the outcome.
self.call_check_file(file_path)
- self.assert_attributes(None, None, None,
- 'Ignoring "gtk2drawing.c": this file is exempt from the style guide.\n')
+ self.assert_attributes(None, None, None, "")
+ self.assertLog(["WARNING: File exempt from style guide. "
+ 'Skipping: "does_exist/gtk2drawing.c"\n'])
def test_check_file_on_non_skipped(self):
# We use a C++ file since by using a CppProcessor, we can check
# that all of the possible information is getting passed to
- # process_file (in particular, the verbosity).
- file_base = "foo"
+ # process_file (in particular, the min_confidence parameter).
+ file_base = "foo_does_exist"
file_extension = "cpp"
file_path = file_base + "." + file_extension
@@ -536,7 +712,97 @@ class StyleCheckerCheckFileTest(unittest.TestCase):
"")
-if __name__ == '__main__':
- import sys
+class StyleCheckerCheckPathsTest(unittest.TestCase):
+
+ """Test the check_paths() method of the StyleChecker class."""
+
+ class MockOs(object):
+
+ class MockPath(object):
+
+ """A mock os.path."""
+
+ def isdir(self, path):
+ return path == "directory"
+
+ def __init__(self):
+ self.path = self.MockPath()
- unittest.main()
+ def walk(self, directory):
+ """A mock of os.walk."""
+ if directory == "directory":
+ dirs = [("dir_path1", [], ["file1", "file2"]),
+ ("dir_path2", [], ["file3"])]
+ return dirs
+ return None
+
+ def setUp(self):
+ self._checked_files = []
+
+ def _mock_check_file(self, file):
+ self._checked_files.append(file)
+
+ def test_check_paths(self):
+ """Test StyleChecker.check_paths()."""
+ checker = StyleChecker(configuration=None)
+ mock_check_file = self._mock_check_file
+ mock_os = self.MockOs()
+
+ # Confirm that checked files is empty at the outset.
+ self.assertEquals(self._checked_files, [])
+ checker.check_paths(["path1", "directory"],
+ mock_check_file=mock_check_file,
+ mock_os=mock_os)
+ self.assertEquals(self._checked_files,
+ ["path1",
+ os.path.join("dir_path1", "file1"),
+ os.path.join("dir_path1", "file2"),
+ os.path.join("dir_path2", "file3")])
+
+
+class PatchCheckerTest(unittest.TestCase):
+
+ """Test the PatchChecker class."""
+
+ class MockStyleChecker(object):
+
+ def __init__(self):
+ self.checked_files = []
+ """A list of (file_path, line_numbers) pairs."""
+
+ def check_file(self, file_path, line_numbers):
+ self.checked_files.append((file_path, line_numbers))
+
+ def setUp(self):
+ style_checker = self.MockStyleChecker()
+ self._style_checker = style_checker
+ self._patch_checker = PatchChecker(style_checker)
+
+ def _call_check_patch(self, patch_string):
+ self._patch_checker.check(patch_string)
+
+ def _assert_checked(self, checked_files):
+ self.assertEquals(self._style_checker.checked_files, checked_files)
+
+ def test_check_patch(self):
+ # The modified line_numbers array for this patch is: [2].
+ self._call_check_patch("""diff --git a/__init__.py b/__init__.py
+index ef65bee..e3db70e 100644
+--- a/__init__.py
++++ b/__init__.py
+@@ -1,1 +1,2 @@
+ # Required for Python to search this directory for module files
++# New line
+""")
+ self._assert_checked([("__init__.py", set([2]))])
+
+ def test_check_patch_with_deletion(self):
+ self._call_check_patch("""Index: __init__.py
+===================================================================
+--- __init__.py (revision 3593)
++++ __init__.py (working copy)
+@@ -1 +0,0 @@
+-foobar
+""")
+ # _mock_check_file should not be called for the deletion patch.
+ self._assert_checked([])
diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers.py b/WebKitTools/Scripts/webkitpy/style/error_handlers.py
index 6bc3f15..5666bfb 100644
--- a/WebKitTools/Scripts/webkitpy/style/error_handlers.py
+++ b/WebKitTools/Scripts/webkitpy/style/error_handlers.py
@@ -40,10 +40,10 @@ Methods:
line_number: The integer line number of the line containing the error.
category: The name of the category of the error, for example
"whitespace/newline".
- confidence: An integer between 1-5 that represents the level of
- confidence in the error. The value 5 means that we are
- certain of the problem, and the value 1 means that it
- could be a legitimate construct.
+ confidence: An integer between 1 and 5 inclusive that represents the
+ application's level of confidence in the error. The value
+ 5 means that we are certain of the problem, and the
+ value 1 means that it could be a legitimate construct.
message: The error message to report.
"""
@@ -56,7 +56,8 @@ class DefaultStyleErrorHandler(object):
"""The default style error handler."""
- def __init__(self, file_path, configuration, increment_error_count):
+ def __init__(self, file_path, configuration, increment_error_count,
+ line_numbers=None):
"""Create a default style error handler.
Args:
@@ -66,16 +67,44 @@ class DefaultStyleErrorHandler(object):
increment_error_count: A function that takes no arguments and
increments the total count of reportable
errors.
+ line_numbers: An array of line numbers of the lines for which
+ style errors should be reported, or None if errors
+ for all lines should be reported. When it is not
+ None, this array normally contains the line numbers
+ corresponding to the modified lines of a patch.
"""
+ if line_numbers is not None:
+ line_numbers = set(line_numbers)
+
self._file_path = file_path
self._configuration = configuration
self._increment_error_count = increment_error_count
+ self._line_numbers = line_numbers
# A string to integer dictionary cache of the number of reportable
# errors per category passed to this instance.
self._category_totals = {}
+ # Useful for unit testing.
+ def __eq__(self, other):
+ """Return whether this instance is equal to another."""
+ if self._configuration != other._configuration:
+ return False
+ if self._file_path != other._file_path:
+ return False
+ if self._increment_error_count != other._increment_error_count:
+ return False
+ if self._line_numbers != other._line_numbers:
+ return False
+
+ return True
+
+ # Useful for unit testing.
+ def __ne__(self, other):
+ # Python does not automatically deduce __ne__ from __eq__.
+ return not self.__eq__(other)
+
def _add_reportable_error(self, category):
"""Increment the error count and return the new category total."""
self._increment_error_count() # Increment the total.
@@ -100,6 +129,12 @@ class DefaultStyleErrorHandler(object):
See the docstring of this module for more information.
"""
+ if (self._line_numbers is not None and
+ line_number not in self._line_numbers):
+ # Then the error occurred in a line that was not modified, so
+ # the error is not reportable.
+ return
+
if not self._configuration.is_reportable(category=category,
confidence_in_error=confidence,
file_path=self._file_path):
@@ -114,7 +149,7 @@ class DefaultStyleErrorHandler(object):
return
self._configuration.write_style_error(category=category,
- confidence=confidence,
+ confidence_in_error=confidence,
file_path=self._file_path,
line_number=line_number,
message=message)
@@ -122,56 +157,3 @@ class DefaultStyleErrorHandler(object):
if category_total == max_reports:
self._configuration.stderr_write("Suppressing further [%s] reports "
"for this file.\n" % category)
-
-
-class PatchStyleErrorHandler(object):
-
- """The style error function for patch files."""
-
- def __init__(self, diff, file_path, configuration, increment_error_count):
- """Create a patch style error handler for the given path.
-
- Args:
- diff: A DiffFile instance.
- Other arguments: see the DefaultStyleErrorHandler.__init__()
- documentation for the other arguments.
-
- """
- self._diff = diff
-
- self._default_error_handler = DefaultStyleErrorHandler(
- configuration=configuration,
- file_path=file_path,
- increment_error_count=
- increment_error_count)
-
- # The line numbers of the modified lines. This is set lazily.
- self._line_numbers = set()
-
- def _get_line_numbers(self):
- """Return the line numbers of the modified lines."""
- if not self._line_numbers:
- for line in self._diff.lines:
- # When deleted line is not set, it means that
- # the line is newly added (or modified).
- if not line[0]:
- self._line_numbers.add(line[1])
-
- return self._line_numbers
-
- def __call__(self, line_number, category, confidence, message):
- """Handle the occurrence of a style error.
-
- This function does not report errors occurring in lines not
- marked as modified or added in the patch.
-
- See the docstring of this module for more information.
-
- """
- if line_number not in self._get_line_numbers():
- # Then the error is not reportable.
- return
-
- self._default_error_handler(line_number, category, confidence,
- message)
-
diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py b/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py
index a39ba2a..05e725a 100644
--- a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py
@@ -25,18 +25,25 @@
import unittest
-from .. style_references import parse_patch
from checker import StyleCheckerConfiguration
from error_handlers import DefaultStyleErrorHandler
-from error_handlers import PatchStyleErrorHandler
from filter import FilterConfiguration
-class StyleErrorHandlerTestBase(unittest.TestCase):
+
+class DefaultStyleErrorHandlerTest(unittest.TestCase):
+
+ """Tests the DefaultStyleErrorHandler class."""
def setUp(self):
self._error_messages = []
self._error_count = 0
+ _category = "whitespace/tab"
+ """The category name for the tests in this class."""
+
+ _file_path = "foo.h"
+ """The file path for the tests in this class."""
+
def _mock_increment_error_count(self):
self._error_count += 1
@@ -51,38 +58,66 @@ class StyleErrorHandlerTestBase(unittest.TestCase):
return StyleCheckerConfiguration(
filter_configuration=filter_configuration,
max_reports_per_category={"whitespace/tab": 2},
+ min_confidence=3,
output_format="vs7",
- stderr_write=self._mock_stderr_write,
- verbosity=3)
-
+ stderr_write=self._mock_stderr_write)
-class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase):
-
- """Tests DefaultStyleErrorHandler class."""
-
- _category = "whitespace/tab"
- """The category name for the tests in this class."""
-
- _file_path = "foo.h"
- """The file path for the tests in this class."""
+ def _error_handler(self, configuration, line_numbers=None):
+ return DefaultStyleErrorHandler(configuration=configuration,
+ file_path=self._file_path,
+ increment_error_count=self._mock_increment_error_count,
+ line_numbers=line_numbers)
def _check_initialized(self):
"""Check that count and error messages are initialized."""
self.assertEquals(0, self._error_count)
self.assertEquals(0, len(self._error_messages))
- def _error_handler(self, configuration):
- return DefaultStyleErrorHandler(configuration=configuration,
- file_path=self._file_path,
- increment_error_count=self._mock_increment_error_count)
-
- def _call_error_handler(self, handle_error, confidence):
+ def _call_error_handler(self, handle_error, confidence, line_number=100):
"""Call the given error handler with a test error."""
- handle_error(line_number=100,
+ handle_error(line_number=line_number,
category=self._category,
confidence=confidence,
message="message")
+ def test_eq__true_return_value(self):
+ """Test the __eq__() method for the return value of True."""
+ handler1 = self._error_handler(configuration=None)
+ handler2 = self._error_handler(configuration=None)
+
+ self.assertTrue(handler1.__eq__(handler2))
+
+ def test_eq__false_return_value(self):
+ """Test the __eq__() method for the return value of False."""
+ def make_handler(configuration=self._style_checker_configuration(),
+ file_path='foo.txt', increment_error_count=lambda: True,
+ line_numbers=[100]):
+ return DefaultStyleErrorHandler(configuration=configuration,
+ file_path=file_path,
+ increment_error_count=increment_error_count,
+ line_numbers=line_numbers)
+
+ handler = make_handler()
+
+ # Establish a baseline for our comparisons below.
+ self.assertTrue(handler.__eq__(make_handler()))
+
+ # Verify that a difference in any argument causes equality to fail.
+ self.assertFalse(handler.__eq__(make_handler(configuration=None)))
+ self.assertFalse(handler.__eq__(make_handler(file_path='bar.txt')))
+ self.assertFalse(handler.__eq__(make_handler(increment_error_count=None)))
+ self.assertFalse(handler.__eq__(make_handler(line_numbers=[50])))
+
+ def test_ne(self):
+ """Test the __ne__() method."""
+ # By default, __ne__ always returns true on different objects.
+ # Thus, check just the distinguishing case to verify that the
+ # code defines __ne__.
+ handler1 = self._error_handler(configuration=None)
+ handler2 = self._error_handler(configuration=None)
+
+ self.assertFalse(handler1.__ne__(handler2))
+
def test_non_reportable_error(self):
"""Test __call__() with a non-reportable error."""
self._check_initialized()
@@ -132,52 +167,21 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase):
self.assertEquals(3, self._error_count)
self.assertEquals(3, len(self._error_messages))
-
-class PatchStyleErrorHandlerTest(StyleErrorHandlerTestBase):
-
- """Tests PatchStyleErrorHandler class."""
-
- _file_path = "__init__.py"
-
- _patch_string = """diff --git a/__init__.py b/__init__.py
-index ef65bee..e3db70e 100644
---- a/__init__.py
-+++ b/__init__.py
-@@ -1 +1,2 @@
- # Required for Python to search this directory for module files
-+# New line
-
-"""
-
- def test_call(self):
- patch_files = parse_patch(self._patch_string)
- diff = patch_files[self._file_path]
-
+ def test_line_numbers(self):
+ """Test the line_numbers parameter."""
+ self._check_initialized()
configuration = self._style_checker_configuration()
-
- handle_error = PatchStyleErrorHandler(diff=diff,
- file_path=self._file_path,
- configuration=configuration,
- increment_error_count=
- self._mock_increment_error_count)
-
- category = "whitespace/tab"
+ error_handler = self._error_handler(configuration,
+ line_numbers=[50])
confidence = 5
- message = "message"
-
- # Confirm error is reportable.
- self.assertTrue(configuration.is_reportable(category,
- confidence,
- self._file_path))
- # Confirm error count initialized to zero.
- self.assertEquals(0, self._error_count)
-
- # Test error in unmodified line (error count does not increment).
- handle_error(1, category, confidence, message)
+ # Error on non-modified line: no error.
+ self._call_error_handler(error_handler, confidence, line_number=60)
self.assertEquals(0, self._error_count)
+ self.assertEquals([], self._error_messages)
- # Test error in modified line (error count increments).
- handle_error(2, category, confidence, message)
+ # Error on modified line: error.
+ self._call_error_handler(error_handler, confidence, line_number=50)
self.assertEquals(1, self._error_count)
-
+ self.assertEquals(self._error_messages,
+ ["foo.h(50): message [whitespace/tab] [5]\n"])
diff --git a/WebKitTools/Scripts/webkitpy/style/filereader.py b/WebKitTools/Scripts/webkitpy/style/filereader.py
new file mode 100644
index 0000000..081e6dc
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/filereader.py
@@ -0,0 +1,148 @@
+# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
+# Copyright (C) 2010 ProFUSION embedded systems
+#
+# 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.
+
+"""Supports reading and processing text files."""
+
+import codecs
+import logging
+import os
+import sys
+
+
+_log = logging.getLogger(__name__)
+
+
+class TextFileReader(object):
+
+ """Supports reading and processing text files.
+
+ Attributes:
+ file_count: The total number of files passed to this instance
+ for processing, including non-text files and files
+ that should be skipped.
+
+ """
+
+ def __init__(self, processor):
+ """Create an instance.
+
+ Arguments:
+ processor: A ProcessorBase instance.
+
+ """
+ self._processor = processor
+ self.file_count = 0
+
+ def _read_lines(self, file_path):
+ """Read the file at a path, and return its lines.
+
+ Raises:
+ IOError: If the file does not exist or cannot be read.
+
+ """
+ # Support the UNIX convention of using "-" for stdin.
+ if file_path == '-':
+ file = codecs.StreamReaderWriter(sys.stdin,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+ else:
+ # We do not open the file with universal newline support
+ # (codecs does not support it anyway), so the resulting
+ # lines contain trailing "\r" characters if we are reading
+ # a file with CRLF endings.
+ file = codecs.open(file_path, 'r', 'utf8', 'replace')
+
+ try:
+ contents = file.read()
+ finally:
+ file.close()
+
+ lines = contents.split('\n')
+ return lines
+
+ def process_file(self, file_path, **kwargs):
+ """Process the given file by calling the processor's process() method.
+
+ Args:
+ file_path: The path of the file to process.
+ **kwargs: Any additional keyword parameters that should be passed
+ to the processor's process() method. The process()
+ method should support these keyword arguments.
+
+ Raises:
+ SystemExit: If no file at file_path exists.
+
+ """
+ self.file_count += 1
+
+ if not self._processor.should_process(file_path):
+ _log.debug("Skipping file: '%s'" % file_path)
+ return
+ _log.debug("Processing file: '%s'" % file_path)
+
+ try:
+ lines = self._read_lines(file_path)
+ except IOError, err:
+ if not os.path.exists(file_path):
+ _log.error("File does not exist: '%s'" % file_path)
+ sys.exit(1)
+
+ message = ("Could not read file. Skipping: '%s'\n %s"
+ % (file_path, err))
+ _log.warn(message)
+ return
+
+ self._processor.process(lines, file_path, **kwargs)
+
+ def _process_directory(self, directory):
+ """Process all files in the given directory, recursively.
+
+ Args:
+ directory: A directory path.
+
+ """
+ for dir_path, dir_names, file_names in os.walk(directory):
+ for file_name in file_names:
+ file_path = os.path.join(dir_path, file_name)
+ self.process_file(file_path)
+
+ def process_paths(self, paths):
+ """Process the given file and directory paths.
+
+ Args:
+ paths: A list of file and directory paths.
+
+ """
+ for path in paths:
+ if os.path.isdir(path):
+ self._process_directory(directory=path)
+ else:
+ self.process_file(path)
diff --git a/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py b/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py
new file mode 100644
index 0000000..8d1a159
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py
@@ -0,0 +1,151 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Contains unit tests for filereader.py."""
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from webkitpy.common.system.logtesting import LoggingTestCase
+from webkitpy.style.checker import ProcessorBase
+from webkitpy.style.filereader import TextFileReader
+
+
+class TextFileReaderTest(LoggingTestCase):
+
+ class MockProcessor(ProcessorBase):
+
+ """A processor for test purposes.
+
+ This processor simply records the parameters passed to its process()
+ method for later checking by the unittest test methods.
+
+ """
+
+ def __init__(self):
+ self.processed = []
+ """The parameters passed for all calls to the process() method."""
+
+ def should_process(self, file_path):
+ return not file_path.endswith('should_not_process.txt')
+
+ def process(self, lines, file_path, test_kwarg=None):
+ self.processed.append((lines, file_path, test_kwarg))
+
+ def setUp(self):
+ LoggingTestCase.setUp(self)
+ processor = TextFileReaderTest.MockProcessor()
+
+ temp_dir = tempfile.mkdtemp()
+
+ self._file_reader = TextFileReader(processor)
+ self._processor = processor
+ self._temp_dir = temp_dir
+
+ def tearDown(self):
+ LoggingTestCase.tearDown(self)
+ shutil.rmtree(self._temp_dir)
+
+ def _create_file(self, rel_path, text):
+ """Create a file with given text and return the path to the file."""
+ file_path = os.path.join(self._temp_dir, rel_path)
+
+ file = open(file_path, 'w')
+ file.write(text)
+ file.close()
+
+ return file_path
+
+ def _passed_to_processor(self):
+ """Return the parameters passed to MockProcessor.process()."""
+ return self._processor.processed
+
+ def _assert_file_reader(self, passed_to_processor, file_count):
+ """Assert the state of the file reader."""
+ self.assertEquals(passed_to_processor, self._passed_to_processor())
+ self.assertEquals(file_count, self._file_reader.file_count)
+
+ def test_process_file__should_not_process(self):
+ self._file_reader.process_file('should_not_process.txt')
+ self._assert_file_reader([], 1)
+
+ def test_process_file__does_not_exist(self):
+ try:
+ self._file_reader.process_file('does_not_exist.txt')
+ except SystemExit, err:
+ self.assertEquals(str(err), '1')
+ else:
+ self.fail('No Exception raised.')
+ self._assert_file_reader([], 1)
+ self.assertLog(["ERROR: File does not exist: 'does_not_exist.txt'\n"])
+
+ def test_process_file__is_dir(self):
+ temp_dir = os.path.join(self._temp_dir, 'test_dir')
+ os.mkdir(temp_dir)
+
+ self._file_reader.process_file(temp_dir)
+
+ # Because the log message below contains exception text, it is
+ # possible that the text varies across platforms. For this reason,
+ # we check only the portion of the log message that we control,
+ # namely the text at the beginning.
+ log_messages = self.logMessages()
+ # We remove the message we are looking at to prevent the tearDown()
+ # from raising an exception when it asserts that no log messages
+ # remain.
+ message = log_messages.pop()
+
+ self.assertTrue(message.startswith('WARNING: Could not read file. '
+ "Skipping: '%s'\n " % temp_dir))
+
+ self._assert_file_reader([], 1)
+
+ def test_process_file__multiple_lines(self):
+ file_path = self._create_file('foo.txt', 'line one\r\nline two\n')
+
+ self._file_reader.process_file(file_path)
+ processed = [(['line one\r', 'line two', ''], file_path, None)]
+ self._assert_file_reader(processed, 1)
+
+ def test_process_file__with_kwarg(self):
+ file_path = self._create_file('foo.txt', 'file contents')
+
+ self._file_reader.process_file(file_path=file_path, test_kwarg='foo')
+ processed = [(['file contents'], file_path, 'foo')]
+ self._assert_file_reader(processed, 1)
+
+ def test_process_paths(self):
+ # We test a list of paths that contains both a file and a directory.
+ dir = os.path.join(self._temp_dir, 'foo_dir')
+ os.mkdir(dir)
+
+ file_path1 = self._create_file('file1.txt', 'foo')
+
+ rel_path = os.path.join('foo_dir', 'file2.txt')
+ file_path2 = self._create_file(rel_path, 'bar')
+
+ self._file_reader.process_paths([dir, file_path1])
+ processed = [(['bar'], file_path2, None),
+ (['foo'], file_path1, None)]
+ self._assert_file_reader(processed, 2)
diff --git a/WebKitTools/Scripts/webkitpy/style/main.py b/WebKitTools/Scripts/webkitpy/style/main.py
new file mode 100644
index 0000000..c933c6d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/main.py
@@ -0,0 +1,130 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+import logging
+import os
+import sys
+
+from webkitpy.common.system.ospath import relpath as _relpath
+
+
+_log = logging.getLogger(__name__)
+
+
+def change_directory(checkout_root, paths, mock_os=None):
+ """Change the working directory to the WebKit checkout root, if possible.
+
+ If every path in the paths parameter is below the checkout root (or if
+ the paths parameter is empty or None), this method changes the current
+ working directory to the checkout root and converts the paths parameter
+ as described below.
+ This allows the paths being checked to be displayed relative to the
+ checkout root, and for path-specific style checks to work as expected.
+ Path-specific checks include whether files should be skipped, whether
+ custom style rules should apply to certain files, etc.
+ If the checkout root is None or the empty string, this method returns
+ the paths parameter unchanged.
+
+ Returns:
+ paths: A copy of the paths parameter -- possibly converted, as follows.
+ If this method changed the current working directory to the
+ checkout root, then the list is the paths parameter converted to
+ normalized paths relative to the checkout root. Otherwise, the
+ paths are not converted.
+
+ Args:
+ paths: A list of paths to the files that should be checked for style.
+ This argument can be None or the empty list if a git commit
+ or all changes under the checkout root should be checked.
+ checkout_root: The path to the root of the WebKit checkout, or None or
+ the empty string if no checkout could be detected.
+ mock_os: A replacement module for unit testing. Defaults to os.
+
+ """
+ os_module = os if mock_os is None else mock_os
+
+ if paths is not None:
+ paths = list(paths)
+
+ if not checkout_root:
+ if not paths:
+ raise Exception("The paths parameter must be non-empty if "
+ "there is no checkout root.")
+
+ # FIXME: Consider trying to detect the checkout root for each file
+ # being checked rather than only trying to detect the checkout
+ # root for the current working directory. This would allow
+ # files to be checked correctly even if the script is being
+ # run from outside any WebKit checkout.
+ #
+ # Moreover, try to find the "source root" for each file
+ # using path-based heuristics rather than using only the
+ # presence of a WebKit checkout. For example, we could
+ # examine parent directories until a directory is found
+ # containing JavaScriptCore, WebCore, WebKit, WebKitSite,
+ # and WebKitTools.
+ # Then log an INFO message saying that a source root not
+ # in a WebKit checkout was found. This will allow us to check
+ # the style of non-scm copies of the source tree (e.g.
+ # nightlies).
+ _log.warn("WebKit checkout root not found:\n"
+ " Path-dependent style checks may not work correctly.\n"
+ " See the help documentation for more info.")
+
+ return paths
+
+ if paths:
+ # Then try converting all of the paths to paths relative to
+ # the checkout root.
+ rel_paths = []
+ for path in paths:
+ rel_path = _relpath(path, checkout_root)
+ if rel_path is None:
+ # Then the path is not below the checkout root. Since all
+ # paths should be interpreted relative to the same root,
+ # do not interpret any of the paths as relative to the
+ # checkout root. Interpret all of them relative to the
+ # current working directory, and do not change the current
+ # working directory.
+ _log.warn(
+"""Path-dependent style checks may not work correctly:
+
+ One of the given paths is outside the WebKit checkout of the current
+ working directory:
+
+ Path: %s
+ Checkout root: %s
+
+ Pass only files below the checkout root to ensure correct results.
+ See the help documentation for more info.
+"""
+ % (path, checkout_root))
+
+ return paths
+ rel_paths.append(rel_path)
+ # If we got here, the conversion was successful.
+ paths = rel_paths
+
+ _log.debug("Changing to checkout root: " + checkout_root)
+ os_module.chdir(checkout_root)
+
+ return paths
diff --git a/WebKitTools/Scripts/webkitpy/style/main_unittest.py b/WebKitTools/Scripts/webkitpy/style/main_unittest.py
new file mode 100644
index 0000000..fe448f5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/main_unittest.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Unit tests for main.py."""
+
+import os
+import unittest
+
+from main import change_directory
+from webkitpy.style_references import LogTesting
+
+
+class ChangeDirectoryTest(unittest.TestCase):
+
+ """Tests change_directory()."""
+
+ _original_directory = "/original"
+ _checkout_root = "/WebKit"
+
+ class _MockOs(object):
+
+ """A mock os module for unit testing."""
+
+ def __init__(self, test_case):
+ self._test_case = test_case
+ self._current_directory = \
+ ChangeDirectoryTest._original_directory
+
+ def chdir(self, current_directory):
+ self._current_directory = current_directory
+
+ def assertCurrentDirectory(self, expected_directory):
+ self._test_case.assertEquals(expected_directory,
+ self._current_directory)
+
+ def setUp(self):
+ self._log = LogTesting.setUp(self)
+ self._mock_os = self._MockOs(self)
+
+ def tearDown(self):
+ self._log.tearDown()
+
+ # This method is a convenient wrapper for change_working_directory() that
+ # passes the mock_os for this unit testing class.
+ def _change_directory(self, paths, checkout_root):
+ return change_directory(paths=paths,
+ checkout_root=checkout_root,
+ mock_os=self._mock_os)
+
+ def _assert_result(self, actual_return_value, expected_return_value,
+ expected_log_messages, expected_current_directory):
+ self.assertEquals(actual_return_value, expected_return_value)
+ self._log.assertMessages(expected_log_messages)
+ self._mock_os.assertCurrentDirectory(expected_current_directory)
+
+ def test_checkout_root_none_paths_none(self):
+ self.assertRaises(Exception, self._change_directory,
+ checkout_root=None, paths=None)
+ self._log.assertMessages([])
+ self._mock_os.assertCurrentDirectory(self._original_directory)
+
+ def test_checkout_root_none(self):
+ paths = self._change_directory(checkout_root=None,
+ paths=["path1"])
+ log_messages = [
+"""WARNING: WebKit checkout root not found:
+ Path-dependent style checks may not work correctly.
+ See the help documentation for more info.
+"""]
+ self._assert_result(paths, ["path1"], log_messages,
+ self._original_directory)
+
+ def test_paths_none(self):
+ paths = self._change_directory(checkout_root=self._checkout_root,
+ paths=None)
+ self._assert_result(paths, None, [], self._checkout_root)
+
+ def test_paths_convertible(self):
+ paths=["/WebKit/foo1.txt",
+ "/WebKit/foo2.txt"]
+ paths = self._change_directory(checkout_root=self._checkout_root,
+ paths=paths)
+ self._assert_result(paths, ["foo1.txt", "foo2.txt"], [],
+ self._checkout_root)
+
+ def test_with_scm_paths_unconvertible(self):
+ paths=["/WebKit/foo1.txt",
+ "/outside/foo2.txt"]
+ paths = self._change_directory(checkout_root=self._checkout_root,
+ paths=paths)
+ log_messages = [
+"""WARNING: Path-dependent style checks may not work correctly:
+
+ One of the given paths is outside the WebKit checkout of the current
+ working directory:
+
+ Path: /outside/foo2.txt
+ Checkout root: /WebKit
+
+ Pass only files below the checkout root to ensure correct results.
+ See the help documentation for more info.
+
+"""]
+ self._assert_result(paths, paths, log_messages,
+ self._original_directory)
diff --git a/WebKitTools/Scripts/webkitpy/style/optparser.py b/WebKitTools/Scripts/webkitpy/style/optparser.py
index 4137c8b..576c16a 100644
--- a/WebKitTools/Scripts/webkitpy/style/optparser.py
+++ b/WebKitTools/Scripts/webkitpy/style/optparser.py
@@ -22,84 +22,83 @@
"""Supports the parsing of command-line options for check-webkit-style."""
-import getopt
+import logging
+from optparse import OptionParser
import os.path
import sys
from filter import validate_filter_rules
# This module should not import anything from checker.py.
+_log = logging.getLogger(__name__)
-def _create_usage(default_options):
- """Return the usage string to display for command help.
+_USAGE = """usage: %prog [--help] [options] [path1] [path2] ...
- Args:
- default_options: A DefaultCommandOptionValues instance.
+Overview:
+ Check coding style according to WebKit style guidelines:
- """
- usage = """
-Syntax: %(program_name)s [--verbose=#] [--git-commit=<SingleCommit>] [--output=vs7]
- [--filter=-x,+y,...] [file] ...
-
- The style guidelines this tries to follow are here:
- http://webkit.org/coding/coding-style.html
+ http://webkit.org/coding/coding-style.html
- Every style error is given a confidence score from 1-5, with 5 meaning
- we are certain of the problem, and 1 meaning it could be a legitimate
- construct. This can miss some errors and does not substitute for
- code review.
+ Path arguments can be files and directories. If neither a git commit nor
+ paths are passed, then all changes in your source control working directory
+ are checked.
- To prevent specific lines from being linted, add a '// NOLINT' comment to the
- end of the line.
+Style errors:
+ This script assigns to every style error a confidence score from 1-5 and
+ a category name. A confidence score of 5 means the error is certainly
+ a problem, and 1 means it could be fine.
- Linted extensions are .cpp, .c and .h. Other file types are ignored.
+ Category names appear in error messages in brackets, for example
+ [whitespace/indent]. See the options section below for an option that
+ displays all available categories and which are reported by default.
- The file parameter is optional and accepts multiple files. Leaving
- out the file parameter applies the check to all files considered changed
- by your source control management system.
+Filters:
+ Use filters to configure what errors to report. Filters are specified using
+ a comma-separated list of boolean filter rules. The script reports errors
+ in a category if the category passes the filter, as described below.
- Flags:
+ All categories start out passing. Boolean filter rules are then evaluated
+ from left to right, with later rules taking precedence. For example, the
+ rule "+foo" passes any category that starts with "foo", and "-foo" fails
+ any such category. The filter input "-whitespace,+whitespace/braces" fails
+ the category "whitespace/tab" and passes "whitespace/braces".
- verbose=#
- A number 1-5 that restricts output to errors with a confidence
- score at or above this value. In particular, the value 1 displays
- all errors. The default is %(default_verbosity)s.
+ Examples: --filter=-whitespace,+whitespace/braces
+ --filter=-whitespace,-runtime/printf,+runtime/printf_format
+ --filter=-,+build/include_what_you_use
- git-commit=<SingleCommit>
- Checks the style of everything from the given commit to the local tree.
+Paths:
+ Certain style-checking behavior depends on the paths relative to
+ the WebKit source root of the files being checked. For example,
+ certain types of errors may be handled differently for files in
+ WebKit/gtk/webkit/ (e.g. by suppressing "readability/naming" errors
+ for files in this directory).
- output=vs7
- The output format, which may be one of
- emacs : to ease emacs parsing
- vs7 : compatible with Visual Studio
- Defaults to "%(default_output_format)s". Other formats are unsupported.
+ Consequently, if the path relative to the source root cannot be
+ determined for a file being checked, then style checking may not
+ work correctly for that file. This can occur, for example, if no
+ WebKit checkout can be found, or if the source root can be detected,
+ but one of the files being checked lies outside the source tree.
- filter=-x,+y,...
- A comma-separated list of boolean filter rules used to filter
- which categories of style guidelines to check. The script checks
- a category if the category passes the filter rules, as follows.
+ If a WebKit checkout can be detected and all files being checked
+ are in the source tree, then all paths will automatically be
+ converted to paths relative to the source root prior to checking.
+ This is also useful for display purposes.
- Any webkit category starts out passing. All filter rules are then
- evaluated left to right, with later rules taking precedence. For
- example, the rule "+foo" passes any category that starts with "foo",
- and "-foo" fails any such category. The filter input "-whitespace,
- +whitespace/braces" fails the category "whitespace/tab" and passes
- "whitespace/braces".
+ Currently, this command can detect the source root only if the
+ command is run from within a WebKit checkout (i.e. if the current
+ working directory is below the root of a checkout). In particular,
+ it is not recommended to run this script from a directory outside
+ a checkout.
- Examples: --filter=-whitespace,+whitespace/braces
- --filter=-whitespace,-runtime/printf,+runtime/printf_format
- --filter=-,+build/include_what_you_use
+ Running this script from a top-level WebKit source directory and
+ checking only files in the source tree will ensure that all style
+ checking behaves correctly -- whether or not a checkout can be
+ detected. This is because all file paths will already be relative
+ to the source root and so will not need to be converted."""
- Category names appear in error messages in brackets, for example
- [whitespace/indent]. To see a list of all categories available to
- %(program_name)s, along with which are enabled by default, pass
- the empty filter as follows:
- --filter=
-""" % {'program_name': os.path.basename(sys.argv[0]),
- 'default_verbosity': default_options.verbosity,
- 'default_output_format': default_options.output_format}
-
- return usage
+_EPILOG = ("This script can miss errors and does not substitute for "
+ "code review.")
# This class should not have knowledge of the flag key names.
@@ -109,26 +108,22 @@ class DefaultCommandOptionValues(object):
Attributes:
output_format: A string that is the default output format.
- verbosity: An integer that is the default verbosity level.
+ min_confidence: An integer that is the default minimum confidence level.
"""
- def __init__(self, output_format, verbosity):
+ def __init__(self, min_confidence, output_format):
+ self.min_confidence = min_confidence
self.output_format = output_format
- self.verbosity = verbosity
-# FIXME: Eliminate support for "extra_flag_values".
-#
# This class should not have knowledge of the flag key names.
class CommandOptionValues(object):
"""Stores the option values passed by the user via the command line.
Attributes:
- extra_flag_values: A string-string dictionary of all flag key-value
- pairs that are not otherwise represented by this
- class. The default is the empty dictionary.
+ is_verbose: A boolean value of whether verbose logging is enabled.
filter_rules: The list of filter rules provided by the user.
These rules are appended to the base rules and
@@ -138,54 +133,52 @@ class CommandOptionValues(object):
git_commit: A string representing the git commit to check.
The default is None.
+ min_confidence: An integer between 1 and 5 inclusive that is the
+ minimum confidence level of style errors to report.
+ The default is 1, which reports all errors.
+
output_format: A string that is the output format. The supported
output formats are "emacs" which emacs can parse
and "vs7" which Microsoft Visual Studio 7 can parse.
- verbosity: An integer between 1-5 inclusive that restricts output
- to errors with a confidence score at or above this value.
- The default is 1, which reports all errors.
-
"""
def __init__(self,
- extra_flag_values=None,
filter_rules=None,
git_commit=None,
- output_format="emacs",
- verbosity=1):
- if extra_flag_values is None:
- extra_flag_values = {}
+ is_verbose=False,
+ min_confidence=1,
+ output_format="emacs"):
if filter_rules is None:
filter_rules = []
+ if (min_confidence < 1) or (min_confidence > 5):
+ raise ValueError('Invalid "min_confidence" parameter: value '
+ "must be an integer between 1 and 5 inclusive. "
+ 'Value given: "%s".' % min_confidence)
+
if output_format not in ("emacs", "vs7"):
raise ValueError('Invalid "output_format" parameter: '
'value must be "emacs" or "vs7". '
'Value given: "%s".' % output_format)
- if (verbosity < 1) or (verbosity > 5):
- raise ValueError('Invalid "verbosity" parameter: '
- "value must be an integer between 1-5 inclusive. "
- 'Value given: "%s".' % verbosity)
-
- self.extra_flag_values = extra_flag_values
self.filter_rules = filter_rules
self.git_commit = git_commit
+ self.is_verbose = is_verbose
+ self.min_confidence = min_confidence
self.output_format = output_format
- self.verbosity = verbosity
# Useful for unit testing.
def __eq__(self, other):
"""Return whether this instance is equal to another."""
- if self.extra_flag_values != other.extra_flag_values:
- return False
if self.filter_rules != other.filter_rules:
return False
if self.git_commit != other.git_commit:
return False
- if self.output_format != other.output_format:
+ if self.is_verbose != other.is_verbose:
return False
- if self.verbosity != other.verbosity:
+ if self.min_confidence != other.min_confidence:
+ return False
+ if self.output_format != other.output_format:
return False
return True
@@ -212,10 +205,9 @@ class ArgumentPrinter(object):
options: A CommandOptionValues instance.
"""
- flags = options.extra_flag_values.copy()
-
+ flags = {}
+ flags['min-confidence'] = options.min_confidence
flags['output'] = options.output_format
- flags['verbose'] = options.verbosity
# Only include the filter flag if user-provided rules are present.
filter_rules = options.filter_rules
if filter_rules:
@@ -231,7 +223,6 @@ class ArgumentPrinter(object):
return flag_string.strip()
-# FIXME: Replace the use of getopt.getopt() with optparse.OptionParser.
class ArgumentParser(object):
# FIXME: Move the documentation of the attributes to the __init__
@@ -256,8 +247,8 @@ class ArgumentParser(object):
all_categories,
default_options,
base_filter_rules=None,
- create_usage=None,
- stderr_write=None):
+ mock_stderr=None,
+ usage=None):
"""Create an ArgumentParser instance.
Args:
@@ -279,31 +270,96 @@ class ArgumentParser(object):
"""
if base_filter_rules is None:
base_filter_rules = []
- if create_usage is None:
- create_usage = _create_usage
- if stderr_write is None:
- stderr_write = sys.stderr.write
+ stderr = sys.stderr if mock_stderr is None else mock_stderr
+ if usage is None:
+ usage = _USAGE
self._all_categories = all_categories
self._base_filter_rules = base_filter_rules
+
# FIXME: Rename these to reflect that they are internal.
- self.create_usage = create_usage
self.default_options = default_options
- self.stderr_write = stderr_write
-
- def _exit_with_usage(self, error_message=''):
- """Exit and print a usage string with an optional error message.
-
- Args:
- error_message: A string that is an error message to print.
-
- """
- usage = self.create_usage(self.default_options)
- self.stderr_write(usage)
+ self.stderr_write = stderr.write
+
+ self._parser = self._create_option_parser(stderr=stderr,
+ usage=usage,
+ default_min_confidence=self.default_options.min_confidence,
+ default_output_format=self.default_options.output_format)
+
+ def _create_option_parser(self, stderr, usage,
+ default_min_confidence, default_output_format):
+ # Since the epilog string is short, it is not necessary to replace
+ # the epilog string with a mock epilog string when testing.
+ # For this reason, we use _EPILOG directly rather than passing it
+ # as an argument like we do for the usage string.
+ parser = OptionParser(usage=usage, epilog=_EPILOG)
+
+ filter_help = ('set a filter to control what categories of style '
+ 'errors to report. Specify a filter using a comma-'
+ 'delimited list of boolean filter rules, for example '
+ '"--filter -whitespace,+whitespace/braces". To display '
+ 'all categories and which are enabled by default, pass '
+ """no value (e.g. '-f ""' or '--filter=').""")
+ parser.add_option("-f", "--filter-rules", metavar="RULES",
+ dest="filter_value", help=filter_help)
+
+ git_help = "check all changes after the given git commit."
+ parser.add_option("-g", "--git-commit", "--git-diff", "--git-since",
+ metavar="COMMIT", dest="git_since", help=git_help,)
+
+ min_confidence_help = ("set the minimum confidence of style errors "
+ "to report. Can be an integer 1-5, with 1 "
+ "displaying all errors. Defaults to %default.")
+ parser.add_option("-m", "--min-confidence", metavar="INT",
+ type="int", dest="min_confidence",
+ default=default_min_confidence,
+ help=min_confidence_help)
+
+ output_format_help = ('set the output format, which can be "emacs" '
+ 'or "vs7" (for Visual Studio). '
+ 'Defaults to "%default".')
+ parser.add_option("-o", "--output-format", metavar="FORMAT",
+ choices=["emacs", "vs7"],
+ dest="output_format", default=default_output_format,
+ help=output_format_help)
+
+ verbose_help = "enable verbose logging."
+ parser.add_option("-v", "--verbose", dest="is_verbose", default=False,
+ action="store_true", help=verbose_help)
+
+ # Override OptionParser's error() method so that option help will
+ # also display when an error occurs. Normally, just the usage
+ # string displays and not option help.
+ parser.error = self._parse_error
+
+ # Override OptionParser's print_help() method so that help output
+ # does not render to the screen while running unit tests.
+ print_help = parser.print_help
+ parser.print_help = lambda: print_help(file=stderr)
+
+ return parser
+
+ def _parse_error(self, error_message):
+ """Print the help string and an error message, and exit."""
+ # The method format_help() includes both the usage string and
+ # the flag options.
+ help = self._parser.format_help()
+ # Separate help from the error message with a single blank line.
+ self.stderr_write(help + "\n")
if error_message:
- sys.exit('\nFATAL ERROR: ' + error_message)
- else:
- sys.exit(1)
+ _log.error(error_message)
+
+ # Since we are using this method to replace/override the Python
+ # module optparse's OptionParser.error() method, we match its
+ # behavior and exit with status code 2.
+ #
+ # As additional background, Python documentation says--
+ #
+ # "Unix programs generally use 2 for command line syntax errors
+ # and 1 for all other kind of errors."
+ #
+ # (from http://docs.python.org/library/sys.html#sys.exit )
+ sys.exit(2)
def _exit_with_categories(self):
"""Exit and print the style categories and default filter rules."""
@@ -335,90 +391,67 @@ class ArgumentParser(object):
filters.append(filter)
return filters
- def parse(self, args, extra_flags=None):
+ def parse(self, args):
"""Parse the command line arguments to check-webkit-style.
Args:
args: A list of command-line arguments as returned by sys.argv[1:].
- extra_flags: A list of flags whose values we want to extract, but
- are not supported by the CommandOptionValues class.
- An example flag "new_flag=". This defaults to the
- empty list.
Returns:
- A tuple of (filenames, options)
+ A tuple of (paths, options)
- filenames: The list of filenames to check.
+ paths: The list of paths to check.
options: A CommandOptionValues instance.
"""
- if extra_flags is None:
- extra_flags = []
-
- output_format = self.default_options.output_format
- verbosity = self.default_options.verbosity
+ (options, paths) = self._parser.parse_args(args=args)
+
+ filter_value = options.filter_value
+ git_commit = options.git_since
+ is_verbose = options.is_verbose
+ min_confidence = options.min_confidence
+ output_format = options.output_format
+
+ if filter_value is not None and not filter_value:
+ # Then the user explicitly passed no filter, for
+ # example "-f ''" or "--filter=".
+ self._exit_with_categories()
+
+ # Validate user-provided values.
+
+ if paths and git_commit:
+ self._parse_error('You cannot provide both paths and a git '
+ 'commit at the same time.')
+
+ # FIXME: Add unit tests.
+ if git_commit and '..' in git_commit:
+ # FIXME: If the range is a "...", the code should find the common
+ # ancestor and start there. See git diff --help for how
+ # "..." usually works.
+ self._parse_error('invalid --git-commit option: option does '
+ 'not support ranges "..": %s' % git_commit)
+
+ min_confidence = int(min_confidence)
+ if (min_confidence < 1) or (min_confidence > 5):
+ self._parse_error('option --min-confidence: invalid integer: '
+ '%s: value must be between 1 and 5'
+ % min_confidence)
+
+ if filter_value:
+ filter_rules = self._parse_filter_flag(filter_value)
+ else:
+ filter_rules = []
- # The flags already supported by the CommandOptionValues class.
- flags = ['help', 'output=', 'verbose=', 'filter=', 'git-commit=']
+ try:
+ validate_filter_rules(filter_rules, self._all_categories)
+ except ValueError, err:
+ self._parse_error(err)
- for extra_flag in extra_flags:
- if extra_flag in flags:
- raise ValueError('Flag \'%(extra_flag)s is duplicated '
- 'or already supported.' %
- {'extra_flag': extra_flag})
- flags.append(extra_flag)
+ options = CommandOptionValues(filter_rules=filter_rules,
+ git_commit=git_commit,
+ is_verbose=is_verbose,
+ min_confidence=min_confidence,
+ output_format=output_format)
- try:
- (opts, filenames) = getopt.getopt(args, '', flags)
- except getopt.GetoptError:
- # FIXME: Settle on an error handling approach: come up
- # with a consistent guideline as to when and whether
- # a ValueError should be raised versus calling
- # sys.exit when needing to interrupt execution.
- self._exit_with_usage('Invalid arguments.')
-
- extra_flag_values = {}
- git_commit = None
- filter_rules = []
-
- for (opt, val) in opts:
- if opt == '--help':
- self._exit_with_usage()
- elif opt == '--output':
- output_format = val
- elif opt == '--verbose':
- verbosity = val
- elif opt == '--git-commit':
- git_commit = val
- elif opt == '--filter':
- if not val:
- self._exit_with_categories()
- filter_rules = self._parse_filter_flag(val)
- else:
- extra_flag_values[opt] = val
-
- # Check validity of resulting values.
- if filenames and (git_commit != None):
- self._exit_with_usage('It is not possible to check files and a '
- 'specific commit at the same time.')
-
- if output_format not in ('emacs', 'vs7'):
- raise ValueError('Invalid --output value "%s": The only '
- 'allowed output formats are emacs and vs7.' %
- output_format)
-
- validate_filter_rules(filter_rules, self._all_categories)
-
- verbosity = int(verbosity)
- if (verbosity < 1) or (verbosity > 5):
- raise ValueError('Invalid --verbose value %s: value must '
- 'be between 1-5.' % verbosity)
-
- options = CommandOptionValues(extra_flag_values=extra_flag_values,
- filter_rules=filter_rules,
- git_commit=git_commit,
- output_format=output_format,
- verbosity=verbosity)
-
- return (filenames, options)
+ return (paths, options)
diff --git a/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py b/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py
index f23c5d1..1c525c6 100644
--- a/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py
@@ -24,22 +24,11 @@
import unittest
-from optparser import _create_usage
-from optparser import ArgumentParser
-from optparser import ArgumentPrinter
-from optparser import CommandOptionValues as ProcessorOptions
-from optparser import DefaultCommandOptionValues
-
-
-class CreateUsageTest(unittest.TestCase):
-
- """Tests the _create_usage() function."""
-
- def test_create_usage(self):
- default_options = DefaultCommandOptionValues(output_format="vs7",
- verbosity=3)
- # Exercise the code path to make sure the function does not error out.
- _create_usage(default_options)
+from webkitpy.common.system.logtesting import LoggingTestCase
+from webkitpy.style.optparser import ArgumentParser
+from webkitpy.style.optparser import ArgumentPrinter
+from webkitpy.style.optparser import CommandOptionValues as ProcessorOptions
+from webkitpy.style.optparser import DefaultCommandOptionValues
class ArgumentPrinterTest(unittest.TestCase):
@@ -50,61 +39,65 @@ class ArgumentPrinterTest(unittest.TestCase):
def _create_options(self,
output_format='emacs',
- verbosity=3,
+ min_confidence=3,
filter_rules=[],
- git_commit=None,
- extra_flag_values={}):
- return ProcessorOptions(extra_flag_values=extra_flag_values,
- filter_rules=filter_rules,
+ git_commit=None):
+ return ProcessorOptions(filter_rules=filter_rules,
git_commit=git_commit,
- output_format=output_format,
- verbosity=verbosity)
+ min_confidence=min_confidence,
+ output_format=output_format)
def test_to_flag_string(self):
- options = self._create_options('vs7', 5, ['+foo', '-bar'], 'git',
- {'a': 0, 'z': 1})
- self.assertEquals('--a=0 --filter=+foo,-bar --git-commit=git '
- '--output=vs7 --verbose=5 --z=1',
+ options = self._create_options('vs7', 5, ['+foo', '-bar'], 'git')
+ self.assertEquals('--filter=+foo,-bar --git-commit=git '
+ '--min-confidence=5 --output=vs7',
self._printer.to_flag_string(options))
# This is to check that --filter and --git-commit do not
# show up when not user-specified.
options = self._create_options()
- self.assertEquals('--output=emacs --verbose=3',
+ self.assertEquals('--min-confidence=3 --output=emacs',
self._printer.to_flag_string(options))
-class ArgumentParserTest(unittest.TestCase):
+class ArgumentParserTest(LoggingTestCase):
"""Test the ArgumentParser class."""
- def _parse(self):
- """Return a default parse() function for testing."""
- return self._create_parser().parse
+ class _MockStdErr(object):
+
+ def write(self, message):
+ # We do not want the usage string or style categories
+ # to print during unit tests, so print nothing.
+ return
+
+ def _parse(self, args):
+ """Call a test parser.parse()."""
+ parser = self._create_parser()
+ return parser.parse(args)
def _create_defaults(self):
"""Return a DefaultCommandOptionValues instance for testing."""
base_filter_rules = ["-", "+whitespace"]
- return DefaultCommandOptionValues(output_format="vs7",
- verbosity=3)
+ return DefaultCommandOptionValues(min_confidence=3,
+ output_format="vs7")
def _create_parser(self):
"""Return an ArgumentParser instance for testing."""
- def stderr_write(message):
- # We do not want the usage string or style categories
- # to print during unit tests, so print nothing.
- return
-
default_options = self._create_defaults()
all_categories = ["build" ,"whitespace"]
+
+ mock_stderr = self._MockStdErr()
+
return ArgumentParser(all_categories=all_categories,
base_filter_rules=[],
default_options=default_options,
- stderr_write=stderr_write)
+ mock_stderr=mock_stderr,
+ usage="test usage")
def test_parse_documentation(self):
- parse = self._parse()
+ parse = self._parse
# FIXME: Test both the printing of the usage string and the
# filter categories help.
@@ -115,56 +108,75 @@ class ArgumentParserTest(unittest.TestCase):
self.assertRaises(SystemExit, parse, ['--filter='])
def test_parse_bad_values(self):
- parse = self._parse()
+ parse = self._parse
# Pass an unsupported argument.
self.assertRaises(SystemExit, parse, ['--bad'])
-
- self.assertRaises(ValueError, parse, ['--verbose=bad'])
- self.assertRaises(ValueError, parse, ['--verbose=0'])
- self.assertRaises(ValueError, parse, ['--verbose=6'])
- parse(['--verbose=1']) # works
- parse(['--verbose=5']) # works
-
- self.assertRaises(ValueError, parse, ['--output=bad'])
+ self.assertLog(['ERROR: no such option: --bad\n'])
+
+ self.assertRaises(SystemExit, parse, ['--git-diff=aa..bb'])
+ self.assertLog(['ERROR: invalid --git-commit option: '
+ 'option does not support ranges "..": aa..bb\n'])
+
+ self.assertRaises(SystemExit, parse, ['--min-confidence=bad'])
+ self.assertLog(['ERROR: option --min-confidence: '
+ "invalid integer value: 'bad'\n"])
+ self.assertRaises(SystemExit, parse, ['--min-confidence=0'])
+ self.assertLog(['ERROR: option --min-confidence: invalid integer: 0: '
+ 'value must be between 1 and 5\n'])
+ self.assertRaises(SystemExit, parse, ['--min-confidence=6'])
+ self.assertLog(['ERROR: option --min-confidence: invalid integer: 6: '
+ 'value must be between 1 and 5\n'])
+ parse(['--min-confidence=1']) # works
+ parse(['--min-confidence=5']) # works
+
+ self.assertRaises(SystemExit, parse, ['--output=bad'])
+ self.assertLog(['ERROR: option --output-format: invalid choice: '
+ "'bad' (choose from 'emacs', 'vs7')\n"])
parse(['--output=vs7']) # works
# Pass a filter rule not beginning with + or -.
- self.assertRaises(ValueError, parse, ['--filter=build'])
+ self.assertRaises(SystemExit, parse, ['--filter=build'])
+ self.assertLog(['ERROR: Invalid filter rule "build": '
+ 'every rule must start with + or -.\n'])
parse(['--filter=+build']) # works
# Pass files and git-commit at the same time.
- self.assertRaises(SystemExit, parse, ['--git-commit=', 'file.txt'])
- # Pass an extra flag already supported.
- self.assertRaises(ValueError, parse, [], ['filter='])
- parse([], ['extra=']) # works
- # Pass an extra flag with typo.
- self.assertRaises(SystemExit, parse, ['--extratypo='], ['extra='])
- parse(['--extra='], ['extra=']) # works
- self.assertRaises(ValueError, parse, [], ['extra=', 'extra='])
-
+ self.assertRaises(SystemExit, parse, ['--git-commit=committish',
+ 'file.txt'])
+ self.assertLog(['ERROR: You cannot provide both paths and '
+ 'a git commit at the same time.\n'])
def test_parse_default_arguments(self):
- parse = self._parse()
+ parse = self._parse
(files, options) = parse([])
self.assertEquals(files, [])
- self.assertEquals(options.output_format, 'vs7')
- self.assertEquals(options.verbosity, 3)
self.assertEquals(options.filter_rules, [])
self.assertEquals(options.git_commit, None)
+ self.assertEquals(options.is_verbose, False)
+ self.assertEquals(options.min_confidence, 3)
+ self.assertEquals(options.output_format, 'vs7')
def test_parse_explicit_arguments(self):
- parse = self._parse()
+ parse = self._parse
# Pass non-default explicit values.
+ (files, options) = parse(['--min-confidence=4'])
+ self.assertEquals(options.min_confidence, 4)
(files, options) = parse(['--output=emacs'])
self.assertEquals(options.output_format, 'emacs')
- (files, options) = parse(['--verbose=4'])
- self.assertEquals(options.verbosity, 4)
+ (files, options) = parse(['-g', 'commit'])
+ self.assertEquals(options.git_commit, 'commit')
(files, options) = parse(['--git-commit=commit'])
self.assertEquals(options.git_commit, 'commit')
+ (files, options) = parse(['--git-diff=commit'])
+ self.assertEquals(options.git_commit, 'commit')
+ (files, options) = parse(['--git-since=commit'])
+ self.assertEquals(options.git_commit, 'commit')
+ (files, options) = parse(['--verbose'])
+ self.assertEquals(options.is_verbose, True)
# Pass user_rules.
(files, options) = parse(['--filter=+build,-whitespace'])
@@ -176,16 +188,8 @@ class ArgumentParserTest(unittest.TestCase):
self.assertEquals(options.filter_rules,
["+build", "-whitespace"])
- # Pass extra flag values.
- (files, options) = parse(['--extra'], ['extra'])
- self.assertEquals(options.extra_flag_values, {'--extra': ''})
- (files, options) = parse(['--extra='], ['extra='])
- self.assertEquals(options.extra_flag_values, {'--extra': ''})
- (files, options) = parse(['--extra=x'], ['extra='])
- self.assertEquals(options.extra_flag_values, {'--extra': 'x'})
-
def test_parse_files(self):
- parse = self._parse()
+ parse = self._parse
(files, options) = parse(['foo.cpp'])
self.assertEquals(files, ['foo.cpp'])
@@ -203,56 +207,60 @@ class CommandOptionValuesTest(unittest.TestCase):
"""Test __init__ constructor."""
# Check default parameters.
options = ProcessorOptions()
- self.assertEquals(options.extra_flag_values, {})
self.assertEquals(options.filter_rules, [])
self.assertEquals(options.git_commit, None)
+ self.assertEquals(options.is_verbose, False)
+ self.assertEquals(options.min_confidence, 1)
self.assertEquals(options.output_format, "emacs")
- self.assertEquals(options.verbosity, 1)
# Check argument validation.
self.assertRaises(ValueError, ProcessorOptions, output_format="bad")
ProcessorOptions(output_format="emacs") # No ValueError: works
ProcessorOptions(output_format="vs7") # works
- self.assertRaises(ValueError, ProcessorOptions, verbosity=0)
- self.assertRaises(ValueError, ProcessorOptions, verbosity=6)
- ProcessorOptions(verbosity=1) # works
- ProcessorOptions(verbosity=5) # works
+ self.assertRaises(ValueError, ProcessorOptions, min_confidence=0)
+ self.assertRaises(ValueError, ProcessorOptions, min_confidence=6)
+ ProcessorOptions(min_confidence=1) # works
+ ProcessorOptions(min_confidence=5) # works
# Check attributes.
- options = ProcessorOptions(extra_flag_values={"extra_value" : 2},
- filter_rules=["+"],
+ options = ProcessorOptions(filter_rules=["+"],
git_commit="commit",
- output_format="vs7",
- verbosity=3)
- self.assertEquals(options.extra_flag_values, {"extra_value" : 2})
+ is_verbose=True,
+ min_confidence=3,
+ output_format="vs7")
self.assertEquals(options.filter_rules, ["+"])
self.assertEquals(options.git_commit, "commit")
+ self.assertEquals(options.is_verbose, True)
+ self.assertEquals(options.min_confidence, 3)
self.assertEquals(options.output_format, "vs7")
- self.assertEquals(options.verbosity, 3)
def test_eq(self):
"""Test __eq__ equality function."""
- # == calls __eq__.
- self.assertTrue(ProcessorOptions() == ProcessorOptions())
-
- # Verify that a difference in any argument causes equality to fail.
- options = ProcessorOptions(extra_flag_values={"extra_value" : 1},
- filter_rules=["+"],
- git_commit="commit",
- output_format="vs7",
- verbosity=1)
- self.assertFalse(options == ProcessorOptions(extra_flag_values=
- {"extra_value" : 2}))
- self.assertFalse(options == ProcessorOptions(filter_rules=["-"]))
- self.assertFalse(options == ProcessorOptions(git_commit="commit2"))
- self.assertFalse(options == ProcessorOptions(output_format="emacs"))
- self.assertFalse(options == ProcessorOptions(verbosity=2))
+ self.assertTrue(ProcessorOptions().__eq__(ProcessorOptions()))
+
+ # Also verify that a difference in any argument causes equality to fail.
+
+ # Explicitly create a ProcessorOptions instance with all default
+ # values. We do this to be sure we are assuming the right default
+ # values in our self.assertFalse() calls below.
+ options = ProcessorOptions(filter_rules=[],
+ git_commit=None,
+ is_verbose=False,
+ min_confidence=1,
+ output_format="emacs")
+ # Verify that we created options correctly.
+ self.assertTrue(options.__eq__(ProcessorOptions()))
+
+ self.assertFalse(options.__eq__(ProcessorOptions(filter_rules=["+"])))
+ self.assertFalse(options.__eq__(ProcessorOptions(git_commit="commit")))
+ self.assertFalse(options.__eq__(ProcessorOptions(is_verbose=True)))
+ self.assertFalse(options.__eq__(ProcessorOptions(min_confidence=2)))
+ self.assertFalse(options.__eq__(ProcessorOptions(output_format="vs7")))
def test_ne(self):
"""Test __ne__ inequality function."""
- # != calls __ne__.
# By default, __ne__ always returns true on different objects.
# Thus, just check the distinguishing case to verify that the
# code defines __ne__.
- self.assertFalse(ProcessorOptions() != ProcessorOptions())
+ self.assertFalse(ProcessorOptions().__ne__(ProcessorOptions()))
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/common.py b/WebKitTools/Scripts/webkitpy/style/processors/common.py
index dbf4bea..30b8fed 100644
--- a/WebKitTools/Scripts/webkitpy/style/processors/common.py
+++ b/WebKitTools/Scripts/webkitpy/style/processors/common.py
@@ -20,7 +20,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Supports style checking not specific to any one processor."""
+"""Supports style checking not specific to any one file type."""
# FIXME: Test this list in the same way that the list of CppProcessor
@@ -33,27 +33,25 @@ categories = set([
])
-def check_no_carriage_return(line, line_number, error):
- """Check that a line does not end with a carriage return.
+class CarriageReturnProcessor(object):
- Returns true if the check is successful (i.e. if the line does not
- end with a carriage return), and false otherwise.
+ """Supports checking for and handling carriage returns."""
- Args:
- line: A string that is the line to check.
- line_number: The line number.
- error: The function to call with any errors found.
+ def __init__(self, handle_style_error):
+ self._handle_style_error = handle_style_error
- """
+ def process(self, lines):
+ """Check for and strip trailing carriage returns from lines."""
+ for line_number in range(len(lines)):
+ if not lines[line_number].endswith("\r"):
+ continue
- if line.endswith("\r"):
- error(line_number,
- "whitespace/carriage_return",
- 1,
- "One or more unexpected \\r (^M) found; "
- "better to use only a \\n")
- return False
-
- return True
+ self._handle_style_error(line_number + 1, # Correct for offset.
+ "whitespace/carriage_return",
+ 1,
+ "One or more unexpected \\r (^M) found; "
+ "better to use only a \\n")
+ lines[line_number] = lines[line_number].rstrip("\r")
+ return lines
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py b/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py
index 9362b65..3dde7b9 100644
--- a/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py
@@ -22,10 +22,9 @@
"""Unit tests for common.py."""
-
import unittest
-from common import check_no_carriage_return
+from common import CarriageReturnProcessor
# FIXME: The unit tests for the cpp, text, and common processors should
@@ -33,13 +32,15 @@ from common import check_no_carriage_return
# mock style error handling code and the code to check that all
# of a processor's categories are covered by the unit tests.
# Such shared code can be located in a shared test file, perhaps
-# ilke this one.
-class CarriageReturnTest(unittest.TestCase):
+# even this file.
+class CarriageReturnProcessorTest(unittest.TestCase):
"""Tests check_no_carriage_return()."""
_category = "whitespace/carriage_return"
_confidence = 1
+ _expected_message = ("One or more unexpected \\r (^M) found; "
+ "better to use only a \\n")
def setUp(self):
self._style_errors = [] # The list of accumulated style errors.
@@ -50,33 +51,44 @@ class CarriageReturnTest(unittest.TestCase):
error = (line_number, category, confidence, message)
self._style_errors.append(error)
- def assert_carriage_return(self, line, is_error):
- """Call check_no_carriage_return() and assert the result."""
- line_number = 100
+ def assert_carriage_return(self, input_lines, expected_lines, error_lines):
+ """Process the given line and assert that the result is correct."""
handle_style_error = self._mock_style_error_handler
- check_no_carriage_return(line, line_number, handle_style_error)
+ processor = CarriageReturnProcessor(handle_style_error)
+ output_lines = processor.process(input_lines)
- expected_message = ("One or more unexpected \\r (^M) found; "
- "better to use only a \\n")
+ # Check both the return value and error messages.
+ self.assertEquals(output_lines, expected_lines)
- if is_error:
- expected_errors = [(line_number, self._category, self._confidence,
- expected_message)]
- self.assertEquals(self._style_errors, expected_errors)
- else:
- self.assertEquals(self._style_errors, [])
+ expected_errors = [(line_number, self._category, self._confidence,
+ self._expected_message)
+ for line_number in error_lines]
+ self.assertEquals(self._style_errors, expected_errors)
def test_ends_with_carriage(self):
- self.assert_carriage_return("carriage return\r", is_error=True)
+ self.assert_carriage_return(["carriage return\r"],
+ ["carriage return"],
+ [1])
def test_ends_with_nothing(self):
- self.assert_carriage_return("no carriage return", is_error=False)
+ self.assert_carriage_return(["no carriage return"],
+ ["no carriage return"],
+ [])
def test_ends_with_newline(self):
- self.assert_carriage_return("no carriage return\n", is_error=False)
-
- def test_ends_with_carriage_newline(self):
- # Check_no_carriage_return only() checks the final character.
- self.assert_carriage_return("carriage\r in a string", is_error=False)
-
+ self.assert_carriage_return(["no carriage return\n"],
+ ["no carriage return\n"],
+ [])
+
+ def test_carriage_in_middle(self):
+ # The CarriageReturnProcessor checks only the final character
+ # of each line.
+ self.assert_carriage_return(["carriage\r in a string"],
+ ["carriage\r in a string"],
+ [])
+
+ def test_multiple_errors(self):
+ self.assert_carriage_return(["line1", "line2\r", "line3\r"],
+ ["line1", "line2", "line3"],
+ [2, 3])
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/cpp.py b/WebKitTools/Scripts/webkitpy/style/processors/cpp.py
index f83ae6a..23be9f9 100644
--- a/WebKitTools/Scripts/webkitpy/style/processors/cpp.py
+++ b/WebKitTools/Scripts/webkitpy/style/processors/cpp.py
@@ -272,18 +272,18 @@ class _FunctionState(object):
"""Tracks current function name and the number of lines in its body.
Attributes:
- verbosity: The verbosity level to use while checking style.
+ min_confidence: The minimum confidence level to use while checking style.
"""
_NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
_TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
- def __init__(self, verbosity):
- self.verbosity = verbosity
+ def __init__(self, min_confidence):
+ self.min_confidence = min_confidence
+ self.current_function = ''
self.in_a_function = False
self.lines_in_function = 0
- self.current_function = ''
def begin(self, function_name):
"""Start analyzing function body.
@@ -311,7 +311,7 @@ class _FunctionState(object):
base_trigger = self._TEST_TRIGGER
else:
base_trigger = self._NORMAL_TRIGGER
- trigger = base_trigger * 2 ** self.verbosity
+ trigger = base_trigger * 2 ** self.min_confidence
if self.lines_in_function > trigger:
error_level = int(math.log(self.lines_in_function / base_trigger, 2))
@@ -642,6 +642,10 @@ def get_header_guard_cpp_variable(filename):
"""
+ # Restores original filename in case that style checker is invoked from Emacs's
+ # flymake.
+ filename = re.sub(r'_flymake\.h$', '.h', filename)
+
return sub(r'[-.\s]', '_', os.path.basename(filename))
@@ -1367,7 +1371,7 @@ def check_spacing(file_extension, clean_lines, line_number, error):
if file_extension == 'cpp':
# C++ should have the & or * beside the type not the variable name.
- matched = match(r'\s*\w+(?<!\breturn)\s+(?P<pointer_operator>\*|\&)\w+', line)
+ matched = match(r'\s*\w+(?<!\breturn|\bdelete)\s+(?P<pointer_operator>\*|\&)\w+', line)
if matched:
error(line_number, 'whitespace/declaration', 3,
'Declaration has space between type name and %s in %s' % (matched.group('pointer_operator'), matched.group(0).strip()))
@@ -1652,7 +1656,7 @@ def check_braces(clean_lines, line_number, error):
# We check if a closed brace has started a line to see if a
# one line control statement was previous.
previous_line = clean_lines.elided[line_number - 2]
- if (previous_line.find('{') > 0
+ if (previous_line.find('{') > 0 and previous_line.find('}') < 0
and search(r'\b(if|for|foreach|while|else)\b', previous_line)):
error(line_number, 'whitespace/braces', 4,
'One line control clauses should not use braces.')
@@ -2769,9 +2773,7 @@ def check_for_include_what_you_use(filename, clean_lines, include_state, error,
# found.
# e.g. If the file name is 'foo_flymake.cpp', we should search for 'foo.h'
# instead of 'foo_flymake.h'
- emacs_flymake_suffix = '_flymake.cpp'
- if abs_filename.endswith(emacs_flymake_suffix):
- abs_filename = abs_filename[:-len(emacs_flymake_suffix)] + '.cpp'
+ abs_filename = re.sub(r'_flymake\.cpp$', '.cpp', abs_filename)
# include_state is modified during iteration, so we iterate over a copy of
# the keys.
@@ -2836,7 +2838,7 @@ def process_line(filename, file_extension,
check_invalid_increment(clean_lines, line, error)
-def _process_lines(filename, file_extension, lines, error, verbosity):
+def _process_lines(filename, file_extension, lines, error, min_confidence):
"""Performs lint checks and reports any errors to the given error function.
Args:
@@ -2850,7 +2852,7 @@ def _process_lines(filename, file_extension, lines, error, verbosity):
['// marker so line numbers end in a known way'])
include_state = _IncludeState()
- function_state = _FunctionState(verbosity)
+ function_state = _FunctionState(min_confidence)
class_state = _ClassState()
file_state = _FileState()
@@ -2948,7 +2950,8 @@ class CppProcessor(object):
'whitespace/todo',
])
- def __init__(self, file_path, file_extension, handle_style_error, verbosity):
+ def __init__(self, file_path, file_extension, handle_style_error,
+ min_confidence):
"""Create a CppProcessor instance.
Args:
@@ -2959,7 +2962,7 @@ class CppProcessor(object):
self.file_extension = file_extension
self.file_path = file_path
self.handle_style_error = handle_style_error
- self.verbosity = verbosity
+ self.min_confidence = min_confidence
# Useful for unit testing.
def __eq__(self, other):
@@ -2970,7 +2973,7 @@ class CppProcessor(object):
return False
if self.handle_style_error != other.handle_style_error:
return False
- if self.verbosity != other.verbosity:
+ if self.min_confidence != other.min_confidence:
return False
return True
@@ -2982,10 +2985,10 @@ class CppProcessor(object):
def process(self, lines):
_process_lines(self.file_path, self.file_extension, lines,
- self.handle_style_error, self.verbosity)
+ self.handle_style_error, self.min_confidence)
# FIXME: Remove this function (requires refactoring unit tests).
-def process_file_data(filename, file_extension, lines, error, verbosity):
- processor = CppProcessor(filename, file_extension, error, verbosity)
+def process_file_data(filename, file_extension, lines, error, min_confidence):
+ processor = CppProcessor(filename, file_extension, error, min_confidence)
processor.process(lines)
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/cpp_unittest.py b/WebKitTools/Scripts/webkitpy/style/processors/cpp_unittest.py
index c786b8e..0a3fe08 100644
--- a/WebKitTools/Scripts/webkitpy/style/processors/cpp_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/processors/cpp_unittest.py
@@ -119,20 +119,21 @@ class CppStyleTestBase(unittest.TestCase):
"""Provides some useful helper functions for cpp_style tests.
Attributes:
- verbosity: An integer that is the current verbosity level for
- the tests.
+ min_confidence: An integer that is the current minimum confidence
+ level for the tests.
"""
- # FIXME: Refactor the unit tests so the verbosity level is passed
+ # FIXME: Refactor the unit tests so the confidence level is passed
# explicitly, just like it is in the real code.
- verbosity = 1;
+ min_confidence = 1;
- # Helper function to avoid needing to explicitly pass verbosity
+ # Helper function to avoid needing to explicitly pass confidence
# in all the unit test calls to cpp_style.process_file_data().
def process_file_data(self, filename, file_extension, lines, error):
- """Call cpp_style.process_file_data() with the current verbosity."""
- return cpp_style.process_file_data(filename, file_extension, lines, error, self.verbosity)
+ """Call cpp_style.process_file_data() with the min_confidence."""
+ return cpp_style.process_file_data(filename, file_extension, lines,
+ error, self.min_confidence)
# Perform lint on single line of input and return the error message.
def perform_single_line_lint(self, code, file_name):
@@ -141,7 +142,7 @@ class CppStyleTestBase(unittest.TestCase):
cpp_style.remove_multi_line_comments(lines, error_collector)
clean_lines = cpp_style.CleansedLines(lines)
include_state = cpp_style._IncludeState()
- function_state = cpp_style._FunctionState(self.verbosity)
+ function_state = cpp_style._FunctionState(self.min_confidence)
ext = file_name[file_name.rfind('.') + 1:]
class_state = cpp_style._ClassState()
file_state = cpp_style._FileState()
@@ -199,7 +200,7 @@ class CppStyleTestBase(unittest.TestCase):
The accumulated errors.
"""
error_collector = ErrorCollector(self.assert_)
- function_state = cpp_style._FunctionState(self.verbosity)
+ function_state = cpp_style._FunctionState(self.min_confidence)
lines = code.split('\n')
cpp_style.remove_multi_line_comments(lines, error_collector)
lines = cpp_style.CleansedLines(lines)
@@ -238,7 +239,7 @@ class CppStyleTestBase(unittest.TestCase):
if re.search(expected_message_re, message):
return
- self.assertEquals(expected_message, messages)
+ self.assertEquals(expected_message_re, messages)
def assert_multi_line_lint(self, code, expected_message, file_name='foo.h'):
file_extension = file_name[file_name.rfind('.') + 1:]
@@ -1547,6 +1548,7 @@ class CppStyleTest(CppStyleTestBase):
'Declaration has space between type name and * in int *b [whitespace/declaration] [3]',
'foo.cpp')
self.assert_lint('return *b;', '', 'foo.cpp')
+ self.assert_lint('delete *b;', '', 'foo.cpp')
self.assert_lint('int *b;', '', 'foo.c')
self.assert_lint('int* b;',
'Declaration has space between * and variable name in int* b [whitespace/declaration] [3]',
@@ -1734,6 +1736,26 @@ class CppStyleTest(CppStyleTestBase):
' [build/header_guard] [5]' % expected_guard),
error_collector.result_list())
+ # Special case for flymake
+ error_collector = ErrorCollector(self.assert_)
+ self.process_file_data('mydir/Foo_flymake.h', 'h',
+ ['#ifndef %s' % expected_guard,
+ '#define %s' % expected_guard,
+ '#endif // %s' % expected_guard],
+ error_collector)
+ for line in error_collector.result_list():
+ if line.find('build/header_guard') != -1:
+ self.fail('Unexpected error: %s' % line)
+
+ error_collector = ErrorCollector(self.assert_)
+ self.process_file_data('mydir/Foo_flymake.h', 'h', [], error_collector)
+ self.assertEquals(
+ 1,
+ error_collector.result_list().count(
+ 'No #ifndef header guard found, suggested CPP variable is: %s'
+ ' [build/header_guard] [5]' % expected_guard),
+ error_collector.result_list())
+
def test_build_printf_format(self):
self.assert_lint(
r'printf("\%%d", value);',
@@ -2227,11 +2249,11 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
cpp_style._FunctionState._TEST_TRIGGER = self.old_test_trigger
# FIXME: Eliminate the need for this function.
- def set_verbosity(self, verbosity):
- """Set new test verbosity and return old test verbosity."""
- old_verbosity = self.verbosity
- self.verbosity = verbosity
- return old_verbosity
+ def set_min_confidence(self, min_confidence):
+ """Set new test confidence and return old test confidence."""
+ old_min_confidence = self.min_confidence
+ self.min_confidence = min_confidence
+ return old_min_confidence
def assert_function_lengths_check(self, code, expected_message):
"""Check warnings for long function bodies are as expected.
@@ -2272,7 +2294,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
lines: Number of lines to generate.
error_level: --v setting for cpp_style.
"""
- trigger_level = self.trigger_lines(self.verbosity)
+ trigger_level = self.trigger_lines(self.min_confidence)
self.assert_function_lengths_check(
'void test(int x)' + self.function_body(lines),
('Small and focused functions are preferred: '
@@ -2355,29 +2377,29 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
'')
def test_function_length_check_definition_below_severity0(self):
- old_verbosity = self.set_verbosity(0)
+ old_min_confidence = self.set_min_confidence(0)
self.assert_function_length_check_definition_ok(self.trigger_lines(0) - 1)
- self.set_verbosity(old_verbosity)
+ self.set_min_confidence(old_min_confidence)
def test_function_length_check_definition_at_severity0(self):
- old_verbosity = self.set_verbosity(0)
+ old_min_confidence = self.set_min_confidence(0)
self.assert_function_length_check_definition_ok(self.trigger_lines(0))
- self.set_verbosity(old_verbosity)
+ self.set_min_confidence(old_min_confidence)
def test_function_length_check_definition_above_severity0(self):
- old_verbosity = self.set_verbosity(0)
+ old_min_confidence = self.set_min_confidence(0)
self.assert_function_length_check_above_error_level(0)
- self.set_verbosity(old_verbosity)
+ self.set_min_confidence(old_min_confidence)
def test_function_length_check_definition_below_severity1v0(self):
- old_verbosity = self.set_verbosity(0)
+ old_min_confidence = self.set_min_confidence(0)
self.assert_function_length_check_below_error_level(1)
- self.set_verbosity(old_verbosity)
+ self.set_min_confidence(old_min_confidence)
def test_function_length_check_definition_at_severity1v0(self):
- old_verbosity = self.set_verbosity(0)
+ old_min_confidence = self.set_min_confidence(0)
self.assert_function_length_check_at_error_level(1)
- self.set_verbosity(old_verbosity)
+ self.set_min_confidence(old_min_confidence)
def test_function_length_check_definition_below_severity1(self):
self.assert_function_length_check_definition_ok(self.trigger_lines(1) - 1)
@@ -2391,7 +2413,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_severity1_plus_blanks(self):
error_level = 1
error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.verbosity)
+ trigger_level = self.trigger_lines(self.min_confidence)
self.assert_function_lengths_check(
'void test_blanks(int x)' + self.function_body(error_lines),
('Small and focused functions are preferred: '
@@ -2403,7 +2425,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_complex_definition_severity1(self):
error_level = 1
error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.verbosity)
+ trigger_level = self.trigger_lines(self.min_confidence)
self.assert_function_lengths_check(
('my_namespace::my_other_namespace::MyVeryLongTypeName*\n'
'my_namespace::my_other_namespace::MyFunction(int arg1, char* arg2)'
@@ -2418,7 +2440,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_severity1_for_test(self):
error_level = 1
error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.verbosity)
+ trigger_level = self.trigger_test_lines(self.min_confidence)
self.assert_function_lengths_check(
'TEST_F(Test, Mutator)' + self.function_body(error_lines),
('Small and focused functions are preferred: '
@@ -2430,7 +2452,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_severity1_for_split_line_test(self):
error_level = 1
error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.verbosity)
+ trigger_level = self.trigger_test_lines(self.min_confidence)
self.assert_function_lengths_check(
('TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,\n'
' FixGoogleUpdate_AllValues_MachineApp)' # note: 4 spaces
@@ -2445,7 +2467,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_severity1_for_bad_test_doesnt_break(self):
error_level = 1
error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.verbosity)
+ trigger_level = self.trigger_test_lines(self.min_confidence)
self.assert_function_lengths_check(
('TEST_F('
+ self.function_body(error_lines)),
@@ -2458,7 +2480,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_severity1_with_embedded_no_lints(self):
error_level = 1
error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.verbosity)
+ trigger_level = self.trigger_lines(self.min_confidence)
self.assert_function_lengths_check(
'void test(int x)' + self.function_body_with_no_lints(error_lines),
('Small and focused functions are preferred: '
@@ -3063,6 +3085,20 @@ class WebKitStyleTest(CppStyleTestBase):
'}\n',
['More than one command on the same line in if [whitespace/parens] [4]',
'One line control clauses should not use braces. [whitespace/braces] [4]'])
+ self.assert_multi_line_lint(
+ 'void func()\n'
+ '{\n'
+ ' while (condition) { }\n'
+ ' return 0;\n'
+ '}\n',
+ '')
+ self.assert_multi_line_lint(
+ 'void func()\n'
+ '{\n'
+ ' for (i = 0; i < 42; i++) { foobar(); }\n'
+ ' return 0;\n'
+ '}\n',
+ 'More than one command on the same line in for [whitespace/parens] [4]')
# 3. An else if statement should be written as an if statement
# when the prior if concludes with a return statement.
@@ -3653,7 +3689,7 @@ class CppProcessorTest(unittest.TestCase):
self.assertEquals(processor.file_extension, "h")
self.assertEquals(processor.file_path, "foo")
self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
- self.assertEquals(processor.verbosity, 3)
+ self.assertEquals(processor.min_confidence, 3)
def test_eq(self):
"""Test __eq__ equality function."""
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/python.py b/WebKitTools/Scripts/webkitpy/style/processors/python.py
new file mode 100644
index 0000000..8ab936d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/processors/python.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Supports checking WebKit style in Python files."""
+
+from ...style_references import pep8
+
+
+class PythonProcessor(object):
+
+ """Processes text lines for checking style."""
+
+ def __init__(self, file_path, handle_style_error):
+ self._file_path = file_path
+ self._handle_style_error = handle_style_error
+
+ def process(self, lines):
+ # Initialize pep8.options, which is necessary for
+ # Checker.check_all() to execute.
+ pep8.process_options(arglist=[self._file_path])
+
+ checker = pep8.Checker(self._file_path)
+
+ def _pep8_handle_error(line_number, offset, text, check):
+ # FIXME: Incorporate the character offset into the error output.
+ # This will require updating the error handler __call__
+ # signature to include an optional "offset" parameter.
+ pep8_code = text[:4]
+ pep8_message = text[5:]
+
+ category = "pep8/" + pep8_code
+
+ self._handle_style_error(line_number, category, 5, pep8_message)
+
+ checker.report_error = _pep8_handle_error
+
+ errors = checker.check_all()
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/python_unittest.py b/WebKitTools/Scripts/webkitpy/style/processors/python_unittest.py
new file mode 100644
index 0000000..3ce3311
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/processors/python_unittest.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Unit tests for python.py."""
+
+import os
+import unittest
+
+from python import PythonProcessor
+
+
+class PythonProcessorTest(unittest.TestCase):
+
+ """Tests the PythonProcessor class."""
+
+ def test_init(self):
+ """Test __init__() method."""
+ def _mock_handle_style_error(self):
+ pass
+
+ processor = PythonProcessor("foo.txt", _mock_handle_style_error)
+ self.assertEquals(processor._file_path, "foo.txt")
+ self.assertEquals(processor._handle_style_error,
+ _mock_handle_style_error)
+
+ def test_process(self):
+ """Test process() method."""
+ errors = []
+
+ def _mock_handle_style_error(line_number, category, confidence,
+ message):
+ error = (line_number, category, confidence, message)
+ errors.append(error)
+
+ current_dir = os.path.dirname(__file__)
+ file_path = os.path.join(current_dir, "python_unittest_input.py")
+
+ processor = PythonProcessor(file_path, _mock_handle_style_error)
+ processor.process(lines=[])
+
+ self.assertEquals(len(errors), 1)
+ self.assertEquals(errors[0],
+ (2, "pep8/W291", 5, "trailing whitespace"))
diff --git a/WebKitTools/Scripts/webkitpy/style/processors/python_unittest_input.py b/WebKitTools/Scripts/webkitpy/style/processors/python_unittest_input.py
new file mode 100644
index 0000000..9f1d118
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/style/processors/python_unittest_input.py
@@ -0,0 +1,2 @@
+# This file is sample input for python_unittest.py and includes a single
+# error which is an extra space at the end of this line.
diff --git a/WebKitTools/Scripts/webkitpy/style/unittests.py b/WebKitTools/Scripts/webkitpy/style/unittests.py
deleted file mode 100644
index 62615ab..0000000
--- a/WebKitTools/Scripts/webkitpy/style/unittests.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
-#
-# 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 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 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.
-
-"""Runs style package unit tests."""
-
-# This module is imported by test-webkitpy.
-
-import sys
-import unittest
-
-from checker_unittest import *
-from error_handlers_unittest import *
-from filter_unittest import *
-from optparser_unittest import *
-from processors.common_unittest import *
-from processors.cpp_unittest import *
-from processors.text_unittest import *
diff --git a/WebKitTools/Scripts/webkitpy/style_references.py b/WebKitTools/Scripts/webkitpy/style_references.py
index 2528c4d..ba2806e 100644
--- a/WebKitTools/Scripts/webkitpy/style_references.py
+++ b/WebKitTools/Scripts/webkitpy/style_references.py
@@ -40,8 +40,20 @@
import os
-from diff_parser import DiffParser
-from scm import detect_scm_system
+from webkitpy.common.checkout.diff_parser import DiffParser
+from webkitpy.common.system.logtesting import LogTesting
+from webkitpy.common.system.logtesting import TestLogStream
+from webkitpy.common.system.logutils import configure_logging
+from webkitpy.common.checkout.scm import detect_scm_system
+from webkitpy.thirdparty.autoinstalled import pep8
+
+
+def detect_checkout():
+ """Return a WebKitCheckout instance, or None if it cannot be found."""
+ cwd = os.path.abspath(os.curdir)
+ scm = detect_scm_system(cwd)
+
+ return None if scm is None else WebKitCheckout(scm)
def parse_patch(patch_string):
@@ -52,16 +64,15 @@ def parse_patch(patch_string):
return patch.files
-class SimpleScm(object):
+class WebKitCheckout(object):
- """Simple facade to SCM for use by style package."""
+ """Simple facade to the SCM class for use by style package."""
- def __init__(self):
- cwd = os.path.abspath('.')
- self._scm = detect_scm_system(cwd)
+ def __init__(self, scm):
+ self._scm = scm
- def checkout_root(self):
- """Return the source control root as an absolute path."""
+ def root_path(self):
+ """Return the checkout root as an absolute path."""
return self._scm.checkout_root
def create_patch(self):
diff --git a/WebKitTools/Scripts/webkitpy/test/__init__.py b/WebKitTools/Scripts/webkitpy/test/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/test/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/test/main.py b/WebKitTools/Scripts/webkitpy/test/main.py
new file mode 100644
index 0000000..daf255f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/test/main.py
@@ -0,0 +1,129 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+"""Contains the entry method for test-webkitpy."""
+
+import logging
+import os
+import sys
+import unittest
+
+import webkitpy
+
+
+_log = logging.getLogger(__name__)
+
+
+class Tester(object):
+
+ """Discovers and runs webkitpy unit tests."""
+
+ def _find_unittest_files(self, webkitpy_dir):
+ """Return a list of paths to all unit-test files."""
+ unittest_paths = [] # Return value.
+
+ for dir_path, dir_names, file_names in os.walk(webkitpy_dir):
+ for file_name in file_names:
+ if not file_name.endswith("_unittest.py"):
+ continue
+ unittest_path = os.path.join(dir_path, file_name)
+ unittest_paths.append(unittest_path)
+
+ return unittest_paths
+
+ def _modules_from_paths(self, webkitpy_dir, paths):
+ """Return a list of fully-qualified module names given paths."""
+ webkitpy_dir = os.path.abspath(webkitpy_dir)
+ webkitpy_name = os.path.split(webkitpy_dir)[1] # Equals "webkitpy".
+
+ prefix_length = len(webkitpy_dir)
+
+ modules = []
+ for path in paths:
+ path = os.path.abspath(path)
+ # This gives us, for example: /common/config/ports_unittest.py
+ rel_path = path[prefix_length:]
+ # This gives us, for example: /common/config/ports_unittest
+ rel_path = os.path.splitext(rel_path)[0]
+
+ parts = []
+ while True:
+ (rel_path, tail) = os.path.split(rel_path)
+ if not tail:
+ break
+ parts.insert(0, tail)
+ # We now have, for example: common.config.ports_unittest
+ parts.insert(0, webkitpy_name) # Put "webkitpy" at the beginning.
+ module = ".".join(parts)
+ modules.append(module)
+
+ return modules
+
+ def run_tests(self, sys_argv):
+ """Run the unit tests in all *_unittest.py modules in webkitpy.
+
+ This method excludes "webkitpy.common.checkout.scm_unittest" unless
+ the --all option is the second element of sys_argv.
+
+ Args:
+ sys_argv: A reference to sys.argv.
+
+ """
+ if len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
+ # Then explicit modules or test names were provided, which
+ # the unittest module is equipped to handle.
+ unittest.main(argv=sys_argv, module=None)
+ # No need to return since unitttest.main() exits.
+
+ # Otherwise, auto-detect all unit tests.
+
+ webkitpy_dir = os.path.dirname(webkitpy.__file__)
+ unittest_paths = self._find_unittest_files(webkitpy_dir)
+
+ modules = self._modules_from_paths(webkitpy_dir, unittest_paths)
+ modules.sort()
+
+ # This is a sanity check to ensure that the unit-test discovery
+ # methods are working.
+ if len(modules) < 1:
+ raise Exception("No unit-test modules found.")
+
+ for module in modules:
+ _log.debug("Found: %s" % module)
+
+ # FIXME: This is a hack, but I'm tired of commenting out the test.
+ # See https://bugs.webkit.org/show_bug.cgi?id=31818
+ if len(sys_argv) > 1 and sys.argv[1] == "--all":
+ sys.argv.remove("--all")
+ else:
+ excluded_module = "webkitpy.common.checkout.scm_unittest"
+ _log.info("Excluding: %s (use --all to include)" % excluded_module)
+ modules.remove(excluded_module)
+
+ sys_argv.extend(modules)
+
+ # We pass None for the module because we do not want the unittest
+ # module to resolve module names relative to a given module.
+ # (This would require importing all of the unittest modules from
+ # this module.) See the loadTestsFromName() method of the
+ # unittest.TestLoader class for more details on this parameter.
+ unittest.main(argv=sys_argv, module=None)
diff --git a/WebKitTools/Scripts/webkitpy/BeautifulSoup.py b/WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py
index 34204e7..34204e7 100644
--- a/WebKitTools/Scripts/webkitpy/BeautifulSoup.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py
new file mode 100644
index 0000000..f1e5334
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# 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 INC. 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 INC. 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.
+
+# This module is required for Python to treat this directory as a package.
+
+"""Autoinstalls third-party code required by WebKit."""
+
+import os
+
+from webkitpy.common.system.autoinstall import AutoInstaller
+
+# Putting the autoinstall code into webkitpy/thirdparty/__init__.py
+# ensures that no autoinstalling occurs until a caller imports from
+# webkitpy.thirdparty. This is useful if the caller wants to configure
+# logging prior to executing autoinstall code.
+
+# FIXME: Ideally, a package should be autoinstalled only if the caller
+# attempts to import from that individual package. This would
+# make autoinstalling lazier than it is currently. This can
+# perhaps be done using Python's import hooks as the original
+# autoinstall implementation did.
+
+# We put auto-installed third-party modules in this directory--
+#
+# webkitpy/thirdparty/autoinstalled
+thirdparty_dir = os.path.dirname(__file__)
+autoinstalled_dir = os.path.join(thirdparty_dir, "autoinstalled")
+
+# We need to download ClientForm since the mechanize package that we download
+# below requires it. The mechanize package uses ClientForm, for example,
+# in _html.py. Since mechanize imports ClientForm in the following way,
+#
+# > import sgmllib, ClientForm
+#
+# the search path needs to include ClientForm. We put ClientForm in
+# its own directory so that we can include it in the search path without
+# including other modules as a side effect.
+clientform_dir = os.path.join(autoinstalled_dir, "clientform")
+installer = AutoInstaller(append_to_search_path=True,
+ target_dir=clientform_dir)
+installer.install(url="http://pypi.python.org/packages/source/C/ClientForm/ClientForm-0.2.10.zip",
+ url_subpath="ClientForm.py")
+
+# The remaining packages do not need to be in the search path, so we create
+# a new AutoInstaller instance that does not append to the search path.
+installer = AutoInstaller(target_dir=autoinstalled_dir)
+
+installer.install(url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
+ url_subpath="mechanize")
+installer.install(url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
+ url_subpath="pep8-0.5.0/pep8.py")
+
+
+rietveld_dir = os.path.join(autoinstalled_dir, "rietveld")
+installer = AutoInstaller(target_dir=rietveld_dir)
+installer.install(url="http://webkit-rietveld.googlecode.com/svn/trunk/static/upload.py",
+ target_name="upload.py")
+
+
+# Since irclib and ircbot are two top-level packages, we need to import
+# them separately. We group them into an irc package for better
+# organization purposes.
+irc_dir = os.path.join(autoinstalled_dir, "irc")
+installer = AutoInstaller(target_dir=irc_dir)
+installer.install(url="http://iweb.dl.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.zip",
+ url_subpath="irclib.py")
+installer.install(url="http://iweb.dl.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.zip",
+ url_subpath="ircbot.py")
+
+readme_path = os.path.join(autoinstalled_dir, "README")
+if not os.path.exists(readme_path):
+ file = open(readme_path, "w")
+ try:
+ file.write("This directory is auto-generated by WebKit and is "
+ "safe to delete.\nIt contains needed third-party Python "
+ "packages automatically downloaded from the web.")
+ finally:
+ file.close()
diff --git a/WebKitTools/Scripts/webkitpy/mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/mock.py
index f6f328e..015c19e 100644
--- a/WebKitTools/Scripts/webkitpy/mock.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/mock.py
@@ -1,309 +1,309 @@
-# mock.py
-# Test tools for mocking and patching.
-# Copyright (C) 2007-2009 Michael Foord
-# E-mail: fuzzyman AT voidspace DOT org DOT uk
-
-# mock 0.6.0
-# http://www.voidspace.org.uk/python/mock/
-
-# Released subject to the BSD License
-# Please see http://www.voidspace.org.uk/python/license.shtml
-
-# 2009-11-25: Licence downloaded from above URL.
-# BEGIN DOWNLOADED LICENSE
-#
-# Copyright (c) 2003-2009, Michael Foord
-# All rights reserved.
-# E-mail : fuzzyman AT voidspace DOT org DOT uk
-#
-# 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 Michael Foord nor the name of Voidspace
-# 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.
-#
-# END DOWNLOADED LICENSE
-
-# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
-# Comments, suggestions and bug reports welcome.
-
-
-__all__ = (
- 'Mock',
- 'patch',
- 'patch_object',
- 'sentinel',
- 'DEFAULT'
-)
-
-__version__ = '0.6.0'
-
-class SentinelObject(object):
- def __init__(self, name):
- self.name = name
-
- def __repr__(self):
- return '<SentinelObject "%s">' % self.name
-
-
-class Sentinel(object):
- def __init__(self):
- self._sentinels = {}
-
- def __getattr__(self, name):
- return self._sentinels.setdefault(name, SentinelObject(name))
-
-
-sentinel = Sentinel()
-
-DEFAULT = sentinel.DEFAULT
-
-class OldStyleClass:
- pass
-ClassType = type(OldStyleClass)
-
-def _is_magic(name):
- return '__%s__' % name[2:-2] == name
-
-def _copy(value):
- if type(value) in (dict, list, tuple, set):
- return type(value)(value)
- return value
-
-
-class Mock(object):
-
- def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
- name=None, parent=None, wraps=None):
- self._parent = parent
- self._name = name
- if spec is not None and not isinstance(spec, list):
- spec = [member for member in dir(spec) if not _is_magic(member)]
-
- self._methods = spec
- self._children = {}
- self._return_value = return_value
- self.side_effect = side_effect
- self._wraps = wraps
-
- self.reset_mock()
-
-
- def reset_mock(self):
- self.called = False
- self.call_args = None
- self.call_count = 0
- self.call_args_list = []
- self.method_calls = []
- for child in self._children.itervalues():
- child.reset_mock()
- if isinstance(self._return_value, Mock):
- self._return_value.reset_mock()
-
-
- def __get_return_value(self):
- if self._return_value is DEFAULT:
- self._return_value = Mock()
- return self._return_value
-
- def __set_return_value(self, value):
- self._return_value = value
-
- return_value = property(__get_return_value, __set_return_value)
-
-
- def __call__(self, *args, **kwargs):
- self.called = True
- self.call_count += 1
- self.call_args = (args, kwargs)
- self.call_args_list.append((args, kwargs))
-
- parent = self._parent
- name = self._name
- while parent is not None:
- parent.method_calls.append((name, args, kwargs))
- if parent._parent is None:
- break
- name = parent._name + '.' + name
- parent = parent._parent
-
- ret_val = DEFAULT
- if self.side_effect is not None:
- if (isinstance(self.side_effect, Exception) or
- isinstance(self.side_effect, (type, ClassType)) and
- issubclass(self.side_effect, Exception)):
- raise self.side_effect
-
- ret_val = self.side_effect(*args, **kwargs)
- if ret_val is DEFAULT:
- ret_val = self.return_value
-
- if self._wraps is not None and self._return_value is DEFAULT:
- return self._wraps(*args, **kwargs)
- if ret_val is DEFAULT:
- ret_val = self.return_value
- return ret_val
-
-
- def __getattr__(self, name):
- if self._methods is not None:
- if name not in self._methods:
- raise AttributeError("Mock object has no attribute '%s'" % name)
- elif _is_magic(name):
- raise AttributeError(name)
-
- if name not in self._children:
- wraps = None
- if self._wraps is not None:
- wraps = getattr(self._wraps, name)
- self._children[name] = Mock(parent=self, name=name, wraps=wraps)
-
- return self._children[name]
-
-
- def assert_called_with(self, *args, **kwargs):
- assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)
-
-
-def _dot_lookup(thing, comp, import_path):
- try:
- return getattr(thing, comp)
- except AttributeError:
- __import__(import_path)
- return getattr(thing, comp)
-
-
-def _importer(target):
- components = target.split('.')
- import_path = components.pop(0)
- thing = __import__(import_path)
-
- for comp in components:
- import_path += ".%s" % comp
- thing = _dot_lookup(thing, comp, import_path)
- return thing
-
-
-class _patch(object):
- def __init__(self, target, attribute, new, spec, create):
- self.target = target
- self.attribute = attribute
- self.new = new
- self.spec = spec
- self.create = create
- self.has_local = False
-
-
- def __call__(self, func):
- if hasattr(func, 'patchings'):
- func.patchings.append(self)
- return func
-
- def patched(*args, **keywargs):
- # don't use a with here (backwards compatability with 2.5)
- extra_args = []
- for patching in patched.patchings:
- arg = patching.__enter__()
- if patching.new is DEFAULT:
- extra_args.append(arg)
- args += tuple(extra_args)
- try:
- return func(*args, **keywargs)
- finally:
- for patching in getattr(patched, 'patchings', []):
- patching.__exit__()
-
- patched.patchings = [self]
- patched.__name__ = func.__name__
- patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
- func.func_code.co_firstlineno)
- return patched
-
-
- def get_original(self):
- target = self.target
- name = self.attribute
- create = self.create
-
- original = DEFAULT
- if _has_local_attr(target, name):
- try:
- original = target.__dict__[name]
- except AttributeError:
- # for instances of classes with slots, they have no __dict__
- original = getattr(target, name)
- elif not create and not hasattr(target, name):
- raise AttributeError("%s does not have the attribute %r" % (target, name))
- return original
-
-
- def __enter__(self):
- new, spec, = self.new, self.spec
- original = self.get_original()
- if new is DEFAULT:
- # XXXX what if original is DEFAULT - shouldn't use it as a spec
- inherit = False
- if spec == True:
- # set spec to the object we are replacing
- spec = original
- if isinstance(spec, (type, ClassType)):
- inherit = True
- new = Mock(spec=spec)
- if inherit:
- new.return_value = Mock(spec=spec)
- self.temp_original = original
- setattr(self.target, self.attribute, new)
- return new
-
-
- def __exit__(self, *_):
- if self.temp_original is not DEFAULT:
- setattr(self.target, self.attribute, self.temp_original)
- else:
- delattr(self.target, self.attribute)
- del self.temp_original
-
-
-def patch_object(target, attribute, new=DEFAULT, spec=None, create=False):
- return _patch(target, attribute, new, spec, create)
-
-
-def patch(target, new=DEFAULT, spec=None, create=False):
- try:
- target, attribute = target.rsplit('.', 1)
- except (TypeError, ValueError):
- raise TypeError("Need a valid target to patch. You supplied: %r" % (target,))
- target = _importer(target)
- return _patch(target, attribute, new, spec, create)
-
-
-
-def _has_local_attr(obj, name):
- try:
- return name in vars(obj)
- except TypeError:
- # objects without a __dict__
- return hasattr(obj, name)
+# mock.py
+# Test tools for mocking and patching.
+# Copyright (C) 2007-2009 Michael Foord
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
+
+# mock 0.6.0
+# http://www.voidspace.org.uk/python/mock/
+
+# Released subject to the BSD License
+# Please see http://www.voidspace.org.uk/python/license.shtml
+
+# 2009-11-25: Licence downloaded from above URL.
+# BEGIN DOWNLOADED LICENSE
+#
+# Copyright (c) 2003-2009, Michael Foord
+# All rights reserved.
+# E-mail : fuzzyman AT voidspace DOT org DOT uk
+#
+# 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 Michael Foord nor the name of Voidspace
+# 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.
+#
+# END DOWNLOADED LICENSE
+
+# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
+# Comments, suggestions and bug reports welcome.
+
+
+__all__ = (
+ 'Mock',
+ 'patch',
+ 'patch_object',
+ 'sentinel',
+ 'DEFAULT'
+)
+
+__version__ = '0.6.0'
+
+class SentinelObject(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return '<SentinelObject "%s">' % self.name
+
+
+class Sentinel(object):
+ def __init__(self):
+ self._sentinels = {}
+
+ def __getattr__(self, name):
+ return self._sentinels.setdefault(name, SentinelObject(name))
+
+
+sentinel = Sentinel()
+
+DEFAULT = sentinel.DEFAULT
+
+class OldStyleClass:
+ pass
+ClassType = type(OldStyleClass)
+
+def _is_magic(name):
+ return '__%s__' % name[2:-2] == name
+
+def _copy(value):
+ if type(value) in (dict, list, tuple, set):
+ return type(value)(value)
+ return value
+
+
+class Mock(object):
+
+ def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
+ name=None, parent=None, wraps=None):
+ self._parent = parent
+ self._name = name
+ if spec is not None and not isinstance(spec, list):
+ spec = [member for member in dir(spec) if not _is_magic(member)]
+
+ self._methods = spec
+ self._children = {}
+ self._return_value = return_value
+ self.side_effect = side_effect
+ self._wraps = wraps
+
+ self.reset_mock()
+
+
+ def reset_mock(self):
+ self.called = False
+ self.call_args = None
+ self.call_count = 0
+ self.call_args_list = []
+ self.method_calls = []
+ for child in self._children.itervalues():
+ child.reset_mock()
+ if isinstance(self._return_value, Mock):
+ self._return_value.reset_mock()
+
+
+ def __get_return_value(self):
+ if self._return_value is DEFAULT:
+ self._return_value = Mock()
+ return self._return_value
+
+ def __set_return_value(self, value):
+ self._return_value = value
+
+ return_value = property(__get_return_value, __set_return_value)
+
+
+ def __call__(self, *args, **kwargs):
+ self.called = True
+ self.call_count += 1
+ self.call_args = (args, kwargs)
+ self.call_args_list.append((args, kwargs))
+
+ parent = self._parent
+ name = self._name
+ while parent is not None:
+ parent.method_calls.append((name, args, kwargs))
+ if parent._parent is None:
+ break
+ name = parent._name + '.' + name
+ parent = parent._parent
+
+ ret_val = DEFAULT
+ if self.side_effect is not None:
+ if (isinstance(self.side_effect, Exception) or
+ isinstance(self.side_effect, (type, ClassType)) and
+ issubclass(self.side_effect, Exception)):
+ raise self.side_effect
+
+ ret_val = self.side_effect(*args, **kwargs)
+ if ret_val is DEFAULT:
+ ret_val = self.return_value
+
+ if self._wraps is not None and self._return_value is DEFAULT:
+ return self._wraps(*args, **kwargs)
+ if ret_val is DEFAULT:
+ ret_val = self.return_value
+ return ret_val
+
+
+ def __getattr__(self, name):
+ if self._methods is not None:
+ if name not in self._methods:
+ raise AttributeError("Mock object has no attribute '%s'" % name)
+ elif _is_magic(name):
+ raise AttributeError(name)
+
+ if name not in self._children:
+ wraps = None
+ if self._wraps is not None:
+ wraps = getattr(self._wraps, name)
+ self._children[name] = Mock(parent=self, name=name, wraps=wraps)
+
+ return self._children[name]
+
+
+ def assert_called_with(self, *args, **kwargs):
+ assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)
+
+
+def _dot_lookup(thing, comp, import_path):
+ try:
+ return getattr(thing, comp)
+ except AttributeError:
+ __import__(import_path)
+ return getattr(thing, comp)
+
+
+def _importer(target):
+ components = target.split('.')
+ import_path = components.pop(0)
+ thing = __import__(import_path)
+
+ for comp in components:
+ import_path += ".%s" % comp
+ thing = _dot_lookup(thing, comp, import_path)
+ return thing
+
+
+class _patch(object):
+ def __init__(self, target, attribute, new, spec, create):
+ self.target = target
+ self.attribute = attribute
+ self.new = new
+ self.spec = spec
+ self.create = create
+ self.has_local = False
+
+
+ def __call__(self, func):
+ if hasattr(func, 'patchings'):
+ func.patchings.append(self)
+ return func
+
+ def patched(*args, **keywargs):
+ # don't use a with here (backwards compatability with 2.5)
+ extra_args = []
+ for patching in patched.patchings:
+ arg = patching.__enter__()
+ if patching.new is DEFAULT:
+ extra_args.append(arg)
+ args += tuple(extra_args)
+ try:
+ return func(*args, **keywargs)
+ finally:
+ for patching in getattr(patched, 'patchings', []):
+ patching.__exit__()
+
+ patched.patchings = [self]
+ patched.__name__ = func.__name__
+ patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
+ func.func_code.co_firstlineno)
+ return patched
+
+
+ def get_original(self):
+ target = self.target
+ name = self.attribute
+ create = self.create
+
+ original = DEFAULT
+ if _has_local_attr(target, name):
+ try:
+ original = target.__dict__[name]
+ except AttributeError:
+ # for instances of classes with slots, they have no __dict__
+ original = getattr(target, name)
+ elif not create and not hasattr(target, name):
+ raise AttributeError("%s does not have the attribute %r" % (target, name))
+ return original
+
+
+ def __enter__(self):
+ new, spec, = self.new, self.spec
+ original = self.get_original()
+ if new is DEFAULT:
+ # XXXX what if original is DEFAULT - shouldn't use it as a spec
+ inherit = False
+ if spec == True:
+ # set spec to the object we are replacing
+ spec = original
+ if isinstance(spec, (type, ClassType)):
+ inherit = True
+ new = Mock(spec=spec)
+ if inherit:
+ new.return_value = Mock(spec=spec)
+ self.temp_original = original
+ setattr(self.target, self.attribute, new)
+ return new
+
+
+ def __exit__(self, *_):
+ if self.temp_original is not DEFAULT:
+ setattr(self.target, self.attribute, self.temp_original)
+ else:
+ delattr(self.target, self.attribute)
+ del self.temp_original
+
+
+def patch_object(target, attribute, new=DEFAULT, spec=None, create=False):
+ return _patch(target, attribute, new, spec, create)
+
+
+def patch(target, new=DEFAULT, spec=None, create=False):
+ try:
+ target, attribute = target.rsplit('.', 1)
+ except (TypeError, ValueError):
+ raise TypeError("Need a valid target to patch. You supplied: %r" % (target,))
+ target = _importer(target)
+ return _patch(target, attribute, new, spec, create)
+
+
+
+def _has_local_attr(obj, name):
+ try:
+ return name in vars(obj)
+ except TypeError:
+ # objects without a __dict__
+ return hasattr(obj, name)
diff --git a/WebKitTools/pywebsocket/COPYING b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING
index ab9d52d..ab9d52d 100644
--- a/WebKitTools/pywebsocket/COPYING
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING
diff --git a/WebKitTools/pywebsocket/MANIFEST.in b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in
index 1925688..1925688 100644
--- a/WebKitTools/pywebsocket/MANIFEST.in
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in
diff --git a/WebKitTools/pywebsocket/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README
index 1f9f05f..1f9f05f 100644
--- a/WebKitTools/pywebsocket/README
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit
new file mode 100644
index 0000000..83e3cee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit
@@ -0,0 +1,14 @@
+This directory contains a copy of the pywebsocket Python module obtained
+from the following location:
+
+http://code.google.com/p/pywebsocket/
+
+This directory currently contains the following version:
+0.4.9.2
+
+The following modifications have been made to this local version:
+minor updates in WebSocketRequestHandler.is_cgi
+
+More information on these local modifications can be found here:
+
+http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket
diff --git a/WebKitTools/pywebsocket/example/echo_client.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py
index 3262a6d..2b976e1 100644
--- a/WebKitTools/pywebsocket/example/echo_client.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py
@@ -163,9 +163,11 @@ def main():
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
parser = OptionParser()
- parser.add_option('-s', '--server_host', dest='server_host', type='string',
+ parser.add_option('-s', '--server-host', '--server_host',
+ dest='server_host', type='string',
default='localhost', help='server host')
- parser.add_option('-p', '--server_port', dest='server_port', type='int',
+ parser.add_option('-p', '--server-port', '--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')
@@ -179,8 +181,8 @@ def main():
default=True, help='suppress messages')
parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
default=False, help='use TLS (wss://)')
- parser.add_option('-k', '--socket_timeout', dest='socket_timeout',
- type='int', default=_TIMEOUT_SEC,
+ parser.add_option('-k', '--socket-timeout', '--socket_timeout',
+ dest='socket_timeout', type='int', default=_TIMEOUT_SEC,
help='Timeout(sec) for sockets')
(options, unused_args) = parser.parse_args()
diff --git a/WebKitTools/pywebsocket/example/echo_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py
index 50cad31..50cad31 100644
--- a/WebKitTools/pywebsocket/example/echo_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt
new file mode 100644
index 0000000..21c4c09
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt
@@ -0,0 +1,11 @@
+# websocket handler map file, used by standalone.py -m option.
+# A line starting with '#' is a comment line.
+# Each line consists of 'alias_resource_path' and 'existing_resource_path'
+# separated by spaces.
+# Aliasing is processed from the top to the bottom of the line, and
+# 'existing_resource_path' must exist before it is aliased.
+# For example,
+# / /echo
+# means that a request to '/' will be handled by handlers for '/echo'.
+/ /echo
+
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py
index 05e80e8..05e80e8 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py
index c52e9eb..c52e9eb 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py
index b86278e..b86278e 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py
index 124b9f1..124b9f1 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/memorizingfile.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py
index 2f8a54e..2f8a54e 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/memorizingfile.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py
index 90ae715..90ae715 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py
diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py
index 8628ff9..f411910 100644
--- a/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py
@@ -272,10 +272,22 @@ class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
"""Test whether self.path corresponds to a CGI script.
- Add extra check that self.path doesn't contains .."""
+ Add extra check that self.path doesn't contains ..
+ Also check if the file is a executable file or not.
+ If the file is not executable, it is handled as static file or dir
+ rather than a CGI script.
+ """
if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self):
if '..' in self.path:
return False
+ # strip query parameter from request path
+ resource_name = self.path.split('?', 2)[0]
+ # convert resource_name into real path name in filesystem.
+ scriptfile = self.translate_path(resource_name)
+ if not os.path.isfile(scriptfile):
+ return False
+ if not self.is_executable(scriptfile):
+ return False
return True
return False
@@ -321,26 +333,32 @@ def _alias_handlers(dispatcher, websock_handlers_map_file):
def _main():
parser = optparse.OptionParser()
+ parser.add_option('-H', '--server-host', '--server_host',
+ dest='server_host',
+ default='',
+ help='server hostname to listen to')
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',
+ parser.add_option('-w', '--websock-handlers', '--websock_handlers',
+ dest='websock_handlers',
default='.',
help='Web Socket handlers root directory.')
- parser.add_option('-m', '--websock_handlers_map_file',
+ parser.add_option('-m', '--websock-handlers-map-file',
+ '--websock_handlers_map_file',
dest='websock_handlers_map_file',
default=None,
help=('Web Socket handlers map file. '
'Each line consists of alias_resource_path and '
'existing_resource_path, separated by spaces.'))
- parser.add_option('-s', '--scan_dir', dest='scan_dir',
+ parser.add_option('-s', '--scan-dir', '--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='.',
+ parser.add_option('-d', '--document-root', '--document_root',
+ dest='document_root', default='.',
help='Document root directory.')
- parser.add_option('-x', '--cgi_paths', dest='cgi_paths',
+ parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
default=None,
help=('CGI paths relative to document_root.'
'Comma-separated. (e.g -x /cgi,/htbin) '
@@ -348,21 +366,22 @@ def _main():
'as CGI programs. Must be executable.'))
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',
+ parser.add_option('-k', '--private-key', '--private_key',
+ dest='private_key',
default='', help='TLS private key file.')
parser.add_option('-c', '--certificate', dest='certificate',
default='', help='TLS certificate file.')
- parser.add_option('-l', '--log_file', dest='log_file',
+ parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
default='', help='Log file.')
- parser.add_option('--log_level', type='choice', dest='log_level',
- default='warn',
+ parser.add_option('--log-level', '--log_level', type='choice',
+ dest='log_level', default='warn',
choices=['debug', 'info', 'warn', 'error', 'critical'],
help='Log level.')
- parser.add_option('--log_max', dest='log_max', type='int',
+ parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
default=_DEFAULT_LOG_MAX_BYTES,
help='Log maximum bytes')
- parser.add_option('--log_count', dest='log_count', type='int',
- default=_DEFAULT_LOG_BACKUP_COUNT,
+ parser.add_option('--log-count', '--log_count', dest='log_count',
+ type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
help='Log backup count')
parser.add_option('--strict', dest='strict', action='store_true',
default=False, help='Strictly check handshake request')
@@ -381,6 +400,18 @@ def _main():
if options.cgi_paths:
CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = \
options.cgi_paths.split(',')
+ if sys.platform in ('cygwin', 'win32'):
+ cygwin_path = None
+ # For Win32 Python, it is expected that CYGWIN_PATH
+ # is set to a directory of cygwin binaries.
+ # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
+ # full path of third_party/cygwin/bin.
+ if 'CYGWIN_PATH' in os.environ:
+ cygwin_path = os.environ['CYGWIN_PATH']
+ util.wrap_popen3_for_win(cygwin_path)
+ def __check_script(scriptpath):
+ return util.get_script_interp(scriptpath, cygwin_path)
+ CGIHTTPServer.executable = __check_script
if options.use_tls:
if not _HAS_OPEN_SSL:
@@ -407,7 +438,8 @@ def _main():
WebSocketRequestHandler.options = options
WebSocketServer.options = options
- server = WebSocketServer(('', options.port), WebSocketRequestHandler)
+ server = WebSocketServer((options.server_host, options.port),
+ WebSocketRequestHandler)
server.serve_forever()
except Exception, e:
logging.critical(str(e))
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py
new file mode 100644
index 0000000..8ec9dca
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py
@@ -0,0 +1,121 @@
+# 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 os
+import re
+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()
+
+
+def prepend_message_to_exception(message, exc):
+ """Prepend message to the exception."""
+
+ exc.args = (message + str(exc),)
+ return
+
+
+def __translate_interp(interp, cygwin_path):
+ """Translate interp program path for Win32 python to run cygwin program
+ (e.g. perl). Note that it doesn't support path that contains space,
+ which is typically true for Unix, where #!-script is written.
+ For Win32 python, cygwin_path is a directory of cygwin binaries.
+
+ Args:
+ interp: interp command line
+ cygwin_path: directory name of cygwin binary, or None
+ Returns:
+ translated interp command line.
+ """
+ if not cygwin_path:
+ return interp
+ m = re.match("^[^ ]*/([^ ]+)( .*)?", interp)
+ if m:
+ cmd = os.path.join(cygwin_path, m.group(1))
+ return cmd + m.group(2)
+ return interp
+
+
+def get_script_interp(script_path, cygwin_path=None):
+ """Gets #!-interpreter command line from the script.
+
+ It also fixes command path. When Cygwin Python is used, e.g. in WebKit,
+ it could run "/usr/bin/perl -wT hello.pl".
+ When Win32 Python is used, e.g. in Chromium, it couldn't. So, fix
+ "/usr/bin/perl" to "<cygwin_path>\perl.exe".
+
+ Args:
+ script_path: pathname of the script
+ cygwin_path: directory name of cygwin binary, or None
+ Returns:
+ #!-interpreter command line, or None if it is not #!-script.
+ """
+ fp = open(script_path)
+ line = fp.readline()
+ fp.close()
+ m = re.match("^#!(.*)", line)
+ if m:
+ return __translate_interp(m.group(1), cygwin_path)
+ return None
+
+def wrap_popen3_for_win(cygwin_path):
+ """Wrap popen3 to support #!-script on Windows.
+
+ Args:
+ cygwin_path: path for cygwin binary if command path is needed to be
+ translated. None if no translation required.
+ """
+ __orig_popen3 = os.popen3
+ def __wrap_popen3(cmd, mode='t', bufsize=-1):
+ cmdline = cmd.split(' ')
+ interp = get_script_interp(cmdline[0], cygwin_path)
+ if interp:
+ cmd = interp + " " + cmd
+ return __orig_popen3(cmd, mode, bufsize)
+ os.popen3 = __wrap_popen3
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/pywebsocket/setup.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py
index 9729322..a34a83b 100644
--- a/WebKitTools/pywebsocket/setup.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py
@@ -56,7 +56,7 @@ setup(author='Yuzo Fujishima',
name=_PACKAGE_NAME,
packages=[_PACKAGE_NAME],
url='http://code.google.com/p/pywebsocket/',
- version='0.4.8',
+ version='0.4.9.2',
)
diff --git a/WebKitTools/pywebsocket/test/config.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py
index 5aaab8c..5aaab8c 100644
--- a/WebKitTools/pywebsocket/test/config.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py
diff --git a/WebKitTools/pywebsocket/test/mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py
index 3b85d64..3b85d64 100644
--- a/WebKitTools/pywebsocket/test/mock.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py
diff --git a/WebKitTools/pywebsocket/test/run_all.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py
index 3885618..3885618 100644
--- a/WebKitTools/pywebsocket/test/run_all.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py
diff --git a/WebKitTools/pywebsocket/test/test_dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py
index 5403228..5403228 100644
--- a/WebKitTools/pywebsocket/test/test_dispatch.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py
diff --git a/WebKitTools/pywebsocket/test/test_handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py
index 8bf07be..8bf07be 100644
--- a/WebKitTools/pywebsocket/test/test_handshake.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py
diff --git a/WebKitTools/pywebsocket/test/test_memorizingfile.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py
index 2de77ba..2de77ba 100644
--- a/WebKitTools/pywebsocket/test/test_memorizingfile.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py
diff --git a/WebKitTools/pywebsocket/test/test_mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py
index 8b137d1..8b137d1 100644
--- a/WebKitTools/pywebsocket/test/test_mock.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py
diff --git a/WebKitTools/pywebsocket/test/test_msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py
index 16b88e0..16b88e0 100644
--- a/WebKitTools/pywebsocket/test/test_msgutil.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py
diff --git a/WebKitTools/pywebsocket/test/test_util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py
index 83e2635..61f0db5 100644
--- a/WebKitTools/pywebsocket/test/test_util.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py
@@ -33,11 +33,14 @@
"""Tests for util module."""
+import os
+import sys
import unittest
import config # This must be imported before mod_pywebsocket.
from mod_pywebsocket import util
+_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata')
class UtilTest(unittest.TestCase):
def test_get_stack_trace(self):
@@ -55,6 +58,18 @@ class UtilTest(unittest.TestCase):
util.prepend_message_to_exception('Hello ', exc)
self.assertEqual('Hello World', str(exc))
+ def test_get_script_interp(self):
+ cygwin_path = 'c:\\cygwin\\bin'
+ cygwin_perl = os.path.join(cygwin_path, 'perl')
+ self.assertEqual(None, util.get_script_interp(
+ os.path.join(_TEST_DATA_DIR, 'README')))
+ self.assertEqual(None, util.get_script_interp(
+ os.path.join(_TEST_DATA_DIR, 'README'), cygwin_path))
+ self.assertEqual('/usr/bin/perl -wT', util.get_script_interp(
+ os.path.join(_TEST_DATA_DIR, 'hello.pl')))
+ self.assertEqual(cygwin_perl + ' -wT', util.get_script_interp(
+ os.path.join(_TEST_DATA_DIR, 'hello.pl'), cygwin_path))
+
if __name__ == '__main__':
unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README
new file mode 100644
index 0000000..c001aa5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README
@@ -0,0 +1 @@
+Test data directory
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py
index 7f87c6a..7f87c6a 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py
index 2c139fa..2c139fa 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py
index b982d02..b982d02 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py
index 17e7be1..17e7be1 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py
index 26352eb..26352eb 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py
index db3ff69..db3ff69 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
index 6bf659b..6bf659b 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
index e0e2e55..e0e2e55 100644
--- a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl
new file mode 100644
index 0000000..9dd01b4
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl
@@ -0,0 +1,2 @@
+#!/usr/bin/perl -wT
+print "Hello\n";
diff --git a/WebKitTools/simplejson/LICENSE.txt b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt
index ad95f29..ad95f29 100644
--- a/WebKitTools/simplejson/LICENSE.txt
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt
diff --git a/WebKitTools/simplejson/README.txt b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt
index 7f726ce..7f726ce 100644
--- a/WebKitTools/simplejson/README.txt
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt
diff --git a/WebKitTools/simplejson/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py
index 38d6229..38d6229 100644
--- a/WebKitTools/simplejson/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py
diff --git a/WebKitTools/simplejson/_speedups.c b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c
index 8f290bb..8f290bb 100644
--- a/WebKitTools/simplejson/_speedups.c
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c
diff --git a/WebKitTools/simplejson/decoder.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py
index a1b53b2..b887b58 100644
--- a/WebKitTools/simplejson/decoder.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py
@@ -3,7 +3,7 @@ Implementation of JSONDecoder
"""
import re
-from simplejson.scanner import Scanner, pattern
+from .scanner import Scanner, pattern
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
diff --git a/WebKitTools/simplejson/encoder.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py
index d29919a..d29919a 100644
--- a/WebKitTools/simplejson/encoder.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py
diff --git a/WebKitTools/simplejson/jsonfilter.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py
index 01ca21d..01ca21d 100644
--- a/WebKitTools/simplejson/jsonfilter.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py
diff --git a/WebKitTools/simplejson/scanner.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py
index 64f4999..64f4999 100644
--- a/WebKitTools/simplejson/scanner.py
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py
diff --git a/WebKitTools/Scripts/webkitpy/tool/__init__.py b/WebKitTools/Scripts/webkitpy/tool/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py b/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py
new file mode 100644
index 0000000..c21fdc6
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import random
+import webkitpy.common.config.irc as config_irc
+
+from webkitpy.common.checkout.changelog import view_source_url
+from webkitpy.tool.bot.queueengine import TerminateQueue
+from webkitpy.common.net.bugzilla import parse_bug_id
+from webkitpy.common.system.executive import ScriptError
+
+# FIXME: Merge with Command?
+class IRCCommand(object):
+ def execute(self, nick, args, tool, sheriff):
+ raise NotImplementedError, "subclasses must implement"
+
+
+class LastGreenRevision(IRCCommand):
+ def execute(self, nick, args, tool, sheriff):
+ return "%s: %s" % (nick,
+ view_source_url(tool.buildbot.last_green_revision()))
+
+
+class Restart(IRCCommand):
+ def execute(self, nick, args, tool, sheriff):
+ tool.irc().post("Restarting...")
+ raise TerminateQueue()
+
+
+class Rollout(IRCCommand):
+ def execute(self, nick, args, tool, sheriff):
+ if len(args) < 2:
+ tool.irc().post("%s: Usage: SVN_REVISION REASON" % nick)
+ return
+ svn_revision = args[0]
+ rollout_reason = " ".join(args[1:])
+ tool.irc().post("Preparing rollout for r%s..." % svn_revision)
+ try:
+ complete_reason = "%s (Requested by %s on %s)." % (
+ rollout_reason, nick, config_irc.channel)
+ bug_id = sheriff.post_rollout_patch(svn_revision, complete_reason)
+ bug_url = tool.bugs.bug_url_for_bug_id(bug_id)
+ tool.irc().post("%s: Created rollout: %s" % (nick, bug_url))
+ except ScriptError, e:
+ tool.irc().post("%s: Failed to create rollout patch:" % nick)
+ tool.irc().post("%s" % e)
+ bug_id = parse_bug_id(e.output)
+ if bug_id:
+ tool.irc().post("Ugg... Might have created %s" %
+ tool.bugs.bug_url_for_bug_id(bug_id))
+
+
+class Hi(IRCCommand):
+ def execute(self, nick, args, tool, sheriff):
+ quips = tool.bugs.quips()
+ quips.append('"Only you can prevent forest fires." -- Smokey the Bear')
+ return random.choice(quips)
diff --git a/WebKitTools/Scripts/webkitpy/patchcollection.py b/WebKitTools/Scripts/webkitpy/tool/bot/patchcollection.py
index 7e8603c..9a2cdfa 100644
--- a/WebKitTools/Scripts/webkitpy/patchcollection.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/patchcollection.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 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
diff --git a/WebKitTools/Scripts/webkitpy/patchcollection_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/patchcollection_unittest.py
index 811fed9..4ec6e25 100644
--- a/WebKitTools/Scripts/webkitpy/patchcollection_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/patchcollection_unittest.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 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
@@ -29,8 +28,8 @@
import unittest
-from webkitpy.mock import Mock
-from webkitpy.patchcollection import PersistentPatchCollection, PersistentPatchCollectionDelegate
+from webkitpy.tool.bot.patchcollection import PersistentPatchCollection, PersistentPatchCollectionDelegate
+from webkitpy.thirdparty.mock import Mock
class TestPersistentPatchCollectionDelegate(PersistentPatchCollectionDelegate):
diff --git a/WebKitTools/Scripts/webkitpy/queueengine.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
index d14177d..2c45ab6 100644
--- a/WebKitTools/Scripts/webkitpy/queueengine.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -34,9 +33,14 @@ import traceback
from datetime import datetime, timedelta
-from webkitpy.executive import ScriptError
-from webkitpy.webkit_logging import log, OutputTee
-from webkitpy.statusserver import StatusServer
+from webkitpy.common.net.statusserver import StatusServer
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.common.system.deprecated_logging import log, OutputTee
+
+
+class TerminateQueue(Exception):
+ pass
+
class QueueEngineDelegate:
def queue_log_path(self):
@@ -66,9 +70,10 @@ class QueueEngineDelegate:
class QueueEngine:
- def __init__(self, name, delegate):
+ def __init__(self, name, delegate, wakeup_event):
self._name = name
self._delegate = delegate
+ self._wakeup_event = wakeup_event
self._output_tee = OutputTee()
log_date_format = "%Y-%m-%d %H:%M:%S"
@@ -101,7 +106,8 @@ class QueueEngine:
# This looks fixed, no?
self._open_work_log(work_item)
try:
- self._delegate.process_work_item(work_item)
+ if not self._delegate.process_work_item(work_item):
+ self._sleep("Unable to process work item.")
except ScriptError, e:
# Use a special exit code to indicate that the error was already
# handled in the child process and we should just keep looping.
@@ -109,6 +115,9 @@ class QueueEngine:
continue
message = "Unexpected failure when landing patch! Please file a bug against webkit-patch.\n%s" % e.message_with_output()
self._delegate.handle_unexpected_error(work_item, message)
+ except TerminateQueue, e:
+ log("\nTerminateQueue exception received.")
+ return 0
except KeyboardInterrupt, e:
log("\nUser terminated queue.")
return 1
@@ -133,12 +142,11 @@ class QueueEngine:
self._output_tee.remove_log(self._work_log)
self._work_log = None
- @classmethod
- def _sleep_message(cls, message):
- 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)
+ def _sleep_message(self, message):
+ wake_time = datetime.now() + timedelta(seconds=self.seconds_to_sleep)
+ return "%s Sleeping until %s (%s)." % (message, wake_time.strftime(self.log_date_format), self.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))
+ self._wakeup_event.wait(self.seconds_to_sleep)
+ self._wakeup_event.clear()
diff --git a/WebKitTools/Scripts/webkitpy/queueengine_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
index a4036ea..626181d 100644
--- a/WebKitTools/Scripts/webkitpy/queueengine_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 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
@@ -30,10 +29,11 @@
import os
import shutil
import tempfile
+import threading
import unittest
-from webkitpy.executive import ScriptError
-from webkitpy.queueengine import QueueEngine, QueueEngineDelegate
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
class LoggingDelegate(QueueEngineDelegate):
def __init__(self, test):
@@ -86,6 +86,7 @@ class LoggingDelegate(QueueEngineDelegate):
def process_work_item(self, work_item):
self.record("process_work_item")
self._test.assertEquals(work_item, "work_item")
+ return True
def handle_unexpected_error(self, work_item, message):
self.record("handle_unexpected_error")
@@ -111,7 +112,7 @@ class NotSafeToProceedDelegate(LoggingDelegate):
class FastQueueEngine(QueueEngine):
def __init__(self, delegate):
- QueueEngine.__init__(self, "fast-queue", delegate)
+ QueueEngine.__init__(self, "fast-queue", delegate, threading.Event())
# No sleep for the wicked.
seconds_to_sleep = 0
@@ -123,7 +124,7 @@ class FastQueueEngine(QueueEngine):
class QueueEngineTest(unittest.TestCase):
def test_trivial(self):
delegate = LoggingDelegate(self)
- work_queue = QueueEngine("trivial-queue", delegate)
+ work_queue = QueueEngine("trivial-queue", delegate, threading.Event())
work_queue.run()
self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "queue_log_path")))
@@ -131,7 +132,7 @@ class QueueEngineTest(unittest.TestCase):
def test_unexpected_error(self):
delegate = ThrowErrorDelegate(self, 3)
- work_queue = QueueEngine("error-queue", delegate)
+ work_queue = QueueEngine("error-queue", delegate, threading.Event())
work_queue.run()
expected_callbacks = LoggingDelegate.expected_callbacks[:]
work_item_index = expected_callbacks.index('process_work_item')
@@ -142,7 +143,7 @@ class QueueEngineTest(unittest.TestCase):
def test_handled_error(self):
delegate = ThrowErrorDelegate(self, QueueEngine.handled_error_code)
- work_queue = QueueEngine("handled-error-queue", delegate)
+ work_queue = QueueEngine("handled-error-queue", delegate, threading.Event())
work_queue.run()
self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py
new file mode 100644
index 0000000..a38c3cf
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2010 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 webkitpy.common.checkout.changelog import view_source_url
+from webkitpy.common.net.bugzilla import parse_bug_id
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.grammar import join_with_separators
+
+
+class Sheriff(object):
+ def __init__(self, tool, sheriffbot):
+ self._tool = tool
+ self._sheriffbot = sheriffbot
+
+ def post_irc_warning(self, commit_info, builders):
+ irc_nicknames = sorted([party.irc_nickname for
+ party in commit_info.responsible_parties()
+ if party.irc_nickname])
+ irc_prefix = ": " if irc_nicknames else ""
+ irc_message = "%s%s%s might have broken %s" % (
+ ", ".join(irc_nicknames),
+ irc_prefix,
+ view_source_url(commit_info.revision()),
+ join_with_separators([builder.name() for builder in builders]))
+
+ self._tool.irc().post(irc_message)
+
+ def post_rollout_patch(self, svn_revision, rollout_reason):
+ # Ensure that svn_revision is a number (and not an option to
+ # create-rollout).
+ try:
+ svn_revision = int(svn_revision)
+ except:
+ raise ScriptError(message="Invalid svn revision number \"%s\"."
+ % svn_revision)
+
+ if rollout_reason.startswith("-"):
+ raise ScriptError(message="The rollout reason may not begin "
+ "with - (\"%s\")." % rollout_reason)
+
+ output = self._sheriffbot.run_webkit_patch([
+ "create-rollout",
+ "--force-clean",
+ # In principle, we should pass --non-interactive here, but it
+ # turns out that create-rollout doesn't need it yet. We can't
+ # pass it prophylactically because we reject unrecognized command
+ # line switches.
+ "--parent-command=sheriff-bot",
+ svn_revision,
+ rollout_reason,
+ ])
+ return parse_bug_id(output)
+
+ def _rollout_reason(self, builders):
+ # FIXME: This should explain which layout tests failed
+ # however, that would require Build objects here, either passed
+ # in through failure_info, or through Builder.latest_build.
+ names = [builder.name() for builder in builders]
+ return "Caused builders %s to fail." % join_with_separators(names)
+
+ def post_automatic_rollout_patch(self, commit_info, builders):
+ # For now we're only posting rollout patches for commit-queue patches.
+ commit_bot_email = "eseidel@chromium.org"
+ if commit_bot_email == commit_info.committer_email():
+ try:
+ self.post_rollout_patch(commit_info.revision(),
+ self._rollout_reason(builders))
+ except ScriptError, e:
+ log("Failed to create-rollout.")
+
+ def post_blame_comment_on_bug(self, commit_info, builders, blame_list):
+ if not commit_info.bug_id():
+ return
+ comment = "%s might have broken %s" % (
+ view_source_url(commit_info.revision()),
+ join_with_separators([builder.name() for builder in builders]))
+ if len(blame_list) > 1:
+ comment += "\nThe following changes are on the blame list:\n"
+ comment += "\n".join(map(view_source_url, blame_list))
+ self._tool.bugs.post_comment_to_bug(commit_info.bug_id(),
+ comment,
+ cc=self._sheriffbot.watchers)
+
+ # FIXME: Should some of this logic be on BuildBot?
+ def provoke_flaky_builders(self, revisions_causing_failures):
+ # We force_build builders that are red but have not "failed" (i.e.,
+ # been red twice). We do this to avoid a deadlock situation where a
+ # flaky test blocks the commit-queue and there aren't any other
+ # patches being landed to re-spin the builder.
+ failed_builders = sum([revisions_causing_failures[key] for
+ key in revisions_causing_failures.keys()], [])
+ failed_builder_names = \
+ set([builder.name() for builder in failed_builders])
+ idle_red_builder_names = \
+ set([builder["name"]
+ for builder in self._tool.buildbot.idle_red_core_builders()])
+
+ # We only want to provoke these builders if they are idle and have not
+ # yet "failed" (i.e., been red twice) to avoid overloading the bots.
+ flaky_builder_names = idle_red_builder_names - failed_builder_names
+
+ for name in flaky_builder_names:
+ flaky_builder = self._tool.buildbot.builder_with_name(name)
+ flaky_builder.force_build(username=self._sheriffbot.name,
+ comments="Probe for flakiness.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
new file mode 100644
index 0000000..dd048a1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import unittest
+
+from webkitpy.common.net.buildbot import Builder
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.bot.sheriff import Sheriff
+from webkitpy.tool.mocktool import MockTool, mock_builder
+
+
+class MockSheriffBot(object):
+ name = "mock-sheriff-bot"
+ watchers = [
+ "watcher@example.com",
+ ]
+
+ def run_webkit_patch(self, args):
+ return "Created bug https://bugs.webkit.org/show_bug.cgi?id=36936\n"
+
+
+class SheriffTest(unittest.TestCase):
+ def test_rollout_reason(self):
+ sheriff = Sheriff(MockTool(), MockSheriffBot())
+ builders = [
+ Builder("Foo", None),
+ Builder("Bar", None),
+ ]
+ reason = "Caused builders Foo and Bar to fail."
+ self.assertEquals(sheriff._rollout_reason(builders), reason)
+
+ def test_post_blame_comment_on_bug(self):
+ def run():
+ sheriff = Sheriff(MockTool(), MockSheriffBot())
+ builders = [
+ Builder("Foo", None),
+ Builder("Bar", None),
+ ]
+ commit_info = Mock()
+ commit_info.bug_id = lambda: None
+ commit_info.revision = lambda: 4321
+ # Should do nothing with no bug_id
+ sheriff.post_blame_comment_on_bug(commit_info, builders, [])
+ sheriff.post_blame_comment_on_bug(commit_info, builders, [2468, 5646])
+ # Should try to post a comment to the bug, but MockTool.bugs does nothing.
+ commit_info.bug_id = lambda: 1234
+ sheriff.post_blame_comment_on_bug(commit_info, builders, [])
+ sheriff.post_blame_comment_on_bug(commit_info, builders, [3432])
+ sheriff.post_blame_comment_on_bug(commit_info, builders, [841, 5646])
+
+ expected_stderr = u"MOCK bug comment: bug_id=1234, cc=['watcher@example.com']\n--- Begin comment ---\\http://trac.webkit.org/changeset/4321 might have broken Foo and Bar\n--- End comment ---\n\nMOCK bug comment: bug_id=1234, cc=['watcher@example.com']\n--- Begin comment ---\\http://trac.webkit.org/changeset/4321 might have broken Foo and Bar\n--- End comment ---\n\nMOCK bug comment: bug_id=1234, cc=['watcher@example.com']\n--- Begin comment ---\\http://trac.webkit.org/changeset/4321 might have broken Foo and Bar\nThe following changes are on the blame list:\nhttp://trac.webkit.org/changeset/841\nhttp://trac.webkit.org/changeset/5646\n--- End comment ---\n\n"
+ OutputCapture().assert_outputs(self, run, expected_stderr=expected_stderr)
+
+ def test_provoke_flaky_builders(self):
+ def run():
+ tool = MockTool()
+ tool.buildbot.light_tree_on_fire()
+ sheriff = Sheriff(tool, MockSheriffBot())
+ revisions_causing_failures = {}
+ sheriff.provoke_flaky_builders(revisions_causing_failures)
+ expected_stderr = "MOCK: force_build: name=Builder2, username=mock-sheriff-bot, comments=Probe for flakiness.\n"
+ OutputCapture().assert_outputs(self, run, expected_stderr=expected_stderr)
+
+ def test_post_blame_comment_on_bug(self):
+ sheriff = Sheriff(MockTool(), MockSheriffBot())
+ builders = [
+ Builder("Foo", None),
+ Builder("Bar", None),
+ ]
+ commit_info = Mock()
+ commit_info.bug_id = lambda: None
+ commit_info.revision = lambda: 4321
+ commit_info.committer = lambda: None
+ commit_info.committer_email = lambda: "foo@example.com"
+ commit_info.reviewer = lambda: None
+ commit_info.author = lambda: None
+ sheriff.post_automatic_rollout_patch(commit_info, builders)
+
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py
new file mode 100644
index 0000000..43aa9c3
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import webkitpy.tool.bot.irc_command as irc_command
+
+from webkitpy.common.net.irc.ircbot import IRCBotDelegate
+from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
+
+
+class _IRCThreadTearoff(IRCBotDelegate):
+ def __init__(self, password, message_queue, wakeup_event):
+ self._password = password
+ self._message_queue = message_queue
+ self._wakeup_event = wakeup_event
+
+ # IRCBotDelegate methods
+
+ def irc_message_received(self, nick, message):
+ self._message_queue.post([nick, message])
+ self._wakeup_event.set()
+
+ def irc_nickname(self):
+ return "sheriffbot"
+
+ def irc_password(self):
+ return self._password
+
+
+class SheriffIRCBot(object):
+ # FIXME: Lame. We should have an auto-registering CommandCenter.
+ commands = {
+ "last-green-revision": irc_command.LastGreenRevision,
+ "restart": irc_command.Restart,
+ "rollout": irc_command.Rollout,
+ "hi": irc_command.Hi,
+ }
+
+ def __init__(self, tool, sheriff):
+ self._tool = tool
+ self._sheriff = sheriff
+ self._message_queue = ThreadedMessageQueue()
+
+ def irc_delegate(self):
+ return _IRCThreadTearoff(self._tool.irc_password,
+ self._message_queue,
+ self._tool.wakeup_event)
+
+ def process_message(self, message):
+ (nick, request) = message
+ tokenized_request = request.strip().split(" ")
+ if not tokenized_request:
+ return
+ command = self.commands.get(tokenized_request[0])
+ if not command:
+ self._tool.irc().post("%s: Available commands: %s" % (
+ nick, ", ".join(self.commands.keys())))
+ return
+ response = command().execute(nick,
+ tokenized_request[1:],
+ self._tool,
+ self._sheriff)
+ if response:
+ self._tool.irc().post(response)
+
+ def process_pending_messages(self):
+ (messages, is_running) = self._message_queue.take_all()
+ for message in messages:
+ self.process_message(message)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py
new file mode 100644
index 0000000..d5116e4
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+import random
+
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.bot.sheriff import Sheriff
+from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot
+from webkitpy.tool.bot.sheriff_unittest import MockSheriffBot
+from webkitpy.tool.mocktool import MockTool
+
+
+def run(message):
+ tool = MockTool()
+ tool.ensure_irc_connected(None)
+ bot = SheriffIRCBot(tool, Sheriff(tool, MockSheriffBot()))
+ bot._message_queue.post(["mock_nick", message])
+ bot.process_pending_messages()
+
+
+class SheriffIRCBotTest(unittest.TestCase):
+ def test_hi(self):
+ random.seed(23324)
+ expected_stderr = 'MOCK: irc.post: "Only you can prevent forest fires." -- Smokey the Bear\n'
+ OutputCapture().assert_outputs(self, run, args=["hi"], expected_stderr=expected_stderr)
+
+ def test_bogus(self):
+ expected_stderr = "MOCK: irc.post: mock_nick: Available commands: rollout, hi, restart, last-green-revision\n"
+ OutputCapture().assert_outputs(self, run, args=["bogus"], expected_stderr=expected_stderr)
+
+ def test_lgr(self):
+ expected_stderr = "MOCK: irc.post: mock_nick: http://trac.webkit.org/changeset/9479\n"
+ OutputCapture().assert_outputs(self, run, args=["last-green-revision"], expected_stderr=expected_stderr)
+
+ def test_rollout(self):
+ expected_stderr = "MOCK: irc.post: Preparing rollout for r21654...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n"
+ OutputCapture().assert_outputs(self, run, args=["rollout 21654 This patch broke the world"], expected_stderr=expected_stderr)
+
+ def test_rollout_bananas(self):
+ expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n"
+ OutputCapture().assert_outputs(self, run, args=["rollout bananas"], expected_stderr=expected_stderr)
+
+ def test_rollout_invalidate_revision(self):
+ expected_stderr = ("MOCK: irc.post: Preparing rollout for r--component=Tools...\n"
+ "MOCK: irc.post: mock_nick: Failed to create rollout patch:\n"
+ "MOCK: irc.post: Invalid svn revision number \"--component=Tools\".\n")
+ OutputCapture().assert_outputs(self, run,
+ args=["rollout "
+ "--component=Tools 21654"],
+ expected_stderr=expected_stderr)
+
+ def test_rollout_invalidate_reason(self):
+ expected_stderr = ("MOCK: irc.post: Preparing rollout for "
+ "r21654...\nMOCK: irc.post: mock_nick: Failed to "
+ "create rollout patch:\nMOCK: irc.post: The rollout"
+ " reason may not begin with - (\"-bad (Requested "
+ "by mock_nick on #webkit).\").\n")
+ OutputCapture().assert_outputs(self, run,
+ args=["rollout "
+ "21654 -bad"],
+ expected_stderr=expected_stderr)
+
+ def test_rollout_no_reason(self):
+ expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n"
+ OutputCapture().assert_outputs(self, run, args=["rollout 21654"], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py b/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py
new file mode 100644
index 0000000..71c3719
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py
@@ -0,0 +1,4 @@
+# Required for Python to search this directory for module files
+
+from webkitpy.tool.commands.prettydiff import PrettyDiff
+# FIXME: Add the rest of the commands here.
diff --git a/WebKitTools/Scripts/webkitpy/commands/abstractsequencedcommand.py b/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py
index 53af5b1..fc5a794 100644
--- a/WebKitTools/Scripts/webkitpy/commands/abstractsequencedcommand.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py
@@ -26,8 +26,8 @@
# (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 webkitpy.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.stepsequence import StepSequence
+from webkitpy.tool.commands.stepsequence import StepSequence
+from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
class AbstractSequencedCommand(AbstractDeclarativeCommand):
diff --git a/WebKitTools/Scripts/webkitpy/commands/commandtest.py b/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py
index a56cb05..887802c 100644
--- a/WebKitTools/Scripts/webkitpy/commands/commandtest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py
@@ -28,11 +28,11 @@
import unittest
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.mocktool import MockTool
+from webkitpy.thirdparty.mock import Mock
class CommandsTest(unittest.TestCase):
- def assert_execute_outputs(self, command, args, expected_stdout="", expected_stderr="", options=Mock(), tool=MockBugzillaTool()):
+ def assert_execute_outputs(self, command, args, expected_stdout="", expected_stderr="", options=Mock(), tool=MockTool()):
command.bind_to_tool(tool)
OutputCapture().assert_outputs(self, command.execute, [options, args, tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
index 49a6862..d960bbe 100644
--- a/WebKitTools/Scripts/webkitpy/commands/download.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -31,18 +31,25 @@ import os
from optparse import make_option
-import webkitpy.steps as steps
+import webkitpy.tool.steps as steps
-from webkitpy.bugzilla import parse_bug_id
-# We could instead use from modules import buildsteps and then prefix every buildstep with "buildsteps."
-from webkitpy.changelogs import ChangeLog
-from webkitpy.commands.abstractsequencedcommand import AbstractSequencedCommand
-from webkitpy.comments import bug_comment_from_commit_text
-from webkitpy.executive import ScriptError
-from webkitpy.grammar import pluralize
-from webkitpy.webkit_logging import error, log
-from webkitpy.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.stepsequence import StepSequence
+from webkitpy.common.checkout.changelog import ChangeLog, view_source_url
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
+from webkitpy.tool.commands.stepsequence import StepSequence
+from webkitpy.tool.comments import bug_comment_from_commit_text
+from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
+from webkitpy.common.system.deprecated_logging import error, log
+
+
+class Update(AbstractSequencedCommand):
+ name = "update"
+ help_text = "Update working copy (used internally)"
+ steps = [
+ steps.CleanWorkingDirectory,
+ steps.Update,
+ ]
class Build(AbstractSequencedCommand):
@@ -74,6 +81,7 @@ class Land(AbstractSequencedCommand):
steps = [
steps.EnsureBuildersAreGreen,
steps.UpdateChangeLogsWithReviewer,
+ steps.ValidateReviewer,
steps.EnsureBuildersAreGreen,
steps.Build,
steps.RunTests,
@@ -86,7 +94,7 @@ If a bug id is provided, or one can be found in the ChangeLog land will update t
def _prepare_state(self, options, args, tool):
return {
- "bug_id" : (args and args[0]) or parse_bug_id(tool.scm().create_patch()),
+ "bug_id" : (args and args[0]) or tool.checkout().bug_id_for_this_commit()
}
@@ -209,6 +217,7 @@ class AbstractPatchLandingCommand(AbstractPatchSequencingCommand):
steps.CleanWorkingDirectory,
steps.Update,
steps.ApplyPatch,
+ steps.ValidateReviewer,
steps.EnsureBuildersAreGreen,
steps.Build,
steps.RunTests,
@@ -240,11 +249,93 @@ class LandFromBug(AbstractPatchLandingCommand, ProcessBugsMixin):
show_in_main_help = True
-class Rollout(AbstractSequencedCommand):
+class AbstractRolloutPrepCommand(AbstractSequencedCommand):
+ argument_names = "REVISION REASON"
+
+ def _commit_info(self, revision):
+ commit_info = self.tool.checkout().commit_info_for_revision(revision)
+ if commit_info and commit_info.bug_id():
+ # Note: Don't print a bug URL here because it will confuse the
+ # SheriffBot because the SheriffBot just greps the output
+ # of create-rollout for bug URLs. It should do better
+ # parsing instead.
+ log("Preparing rollout for bug %s." % commit_info.bug_id())
+ return commit_info
+ log("Unable to parse bug number from diff.")
+
+ def _prepare_state(self, options, args, tool):
+ revision = args[0]
+ commit_info = self._commit_info(revision)
+ cc_list = sorted([party.bugzilla_email()
+ for party in commit_info.responsible_parties()
+ if party.bugzilla_email()])
+ return {
+ "revision": revision,
+ "bug_id": commit_info.bug_id(),
+ # FIXME: We should used the list as the canonical representation.
+ "bug_cc": ",".join(cc_list),
+ "reason": args[1],
+ }
+
+
+class PrepareRollout(AbstractRolloutPrepCommand):
+ name = "prepare-rollout"
+ help_text = "Revert the given revision in the working copy and prepare ChangeLogs with revert reason"
+ long_help = """Updates the working copy.
+Applies the inverse diff for the provided revision.
+Creates an appropriate rollout ChangeLog, including a trac link and bug link.
+"""
+ steps = [
+ steps.CleanWorkingDirectory,
+ steps.Update,
+ steps.RevertRevision,
+ steps.PrepareChangeLogForRevert,
+ ]
+
+
+class CreateRollout(AbstractRolloutPrepCommand):
+ name = "create-rollout"
+ help_text = "Creates a bug to track a broken SVN revision and uploads a rollout patch."
+ steps = [
+ steps.CleanWorkingDirectory,
+ steps.Update,
+ steps.RevertRevision,
+ steps.CreateBug,
+ steps.PrepareChangeLogForRevert,
+ steps.PostDiffForRevert,
+ ]
+
+ def _prepare_state(self, options, args, tool):
+ state = AbstractRolloutPrepCommand._prepare_state(self, options, args, tool)
+ # Currently, state["bug_id"] points to the bug that caused the
+ # regression. We want to create a new bug that blocks the old bug
+ # so we move state["bug_id"] to state["bug_blocked"] and delete the
+ # old state["bug_id"] so that steps.CreateBug will actually create
+ # the new bug that we want (and subsequently store its bug id into
+ # state["bug_id"])
+ state["bug_blocked"] = state["bug_id"]
+ del state["bug_id"]
+ state["bug_title"] = "REGRESSION(r%s): %s" % (state["revision"], state["reason"])
+ state["bug_description"] = "%s broke the build:\n%s" % (view_source_url(state["revision"]), state["reason"])
+ # FIXME: If we had more context here, we could link to other open bugs
+ # that mention the test that regressed.
+ if options.parent_command == "sheriff-bot":
+ state["bug_description"] += """
+
+This is an automatic bug report generated by the sheriff-bot. If this bug
+report was created because of a flaky test, please file a bug for the flaky
+test (if we don't already have one on file) and dup this bug against that bug
+so that we can track how often these flaky tests case pain.
+
+"Only you can prevent forest fires." -- Smokey the Bear
+"""
+ return state
+
+
+class Rollout(AbstractRolloutPrepCommand):
name = "rollout"
show_in_main_help = True
help_text = "Revert the given revision in the working copy and optionally commit the revert and re-open the original bug"
- argument_names = "REVISION REASON"
long_help = """Updates the working copy.
Applies the inverse diff for the provided revision.
Creates an appropriate rollout ChangeLog, including a trac link and bug link.
@@ -258,27 +349,7 @@ Commits the revert and updates the bug (including re-opening the bug if necessar
steps.PrepareChangeLogForRevert,
steps.EditChangeLog,
steps.ConfirmDiff,
- steps.CompleteRollout,
+ steps.Build,
+ steps.Commit,
+ steps.ReopenBugAfterRollout,
]
-
- @staticmethod
- def _parse_bug_id_from_revision_diff(tool, revision):
- original_diff = tool.scm().diff_for_revision(revision)
- return parse_bug_id(original_diff)
-
- def execute(self, options, args, tool):
- revision = args[0]
- reason = args[1]
- bug_id = self._parse_bug_id_from_revision_diff(tool, revision)
- if options.complete_rollout:
- if bug_id:
- log("Will re-open bug %s after rollout." % bug_id)
- else:
- log("Failed to parse bug number from diff. No bugs will be updated/reopened after the rollout.")
-
- state = {
- "revision" : revision,
- "bug_id" : bug_id,
- "reason" : reason,
- }
- self._sequence.run_and_handle_errors(tool, options, state)
diff --git a/WebKitTools/Scripts/webkitpy/commands/download_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
index f60c5b8..926037c 100644
--- a/WebKitTools/Scripts/webkitpy/commands/download_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
@@ -26,9 +26,10 @@
# (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 webkitpy.commands.commandtest import CommandsTest
-from webkitpy.commands.download import *
-from webkitpy.mock import Mock
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.commandtest import CommandsTest
+from webkitpy.tool.commands.download import *
+
class DownloadCommandsTest(CommandsTest):
def _default_options(self):
@@ -42,7 +43,6 @@ class DownloadCommandsTest(CommandsTest):
options.build = True
options.test = True
options.close_bug = True
- options.complete_rollout = False
return options
def test_build(self):
@@ -116,12 +116,32 @@ Not closing bug 42 as attachment 197 has review=+. Assuming there are more patc
"""
self.assert_execute_outputs(LandFromBug(), [42], options=self._default_options(), expected_stderr=expected_stderr)
+ def test_prepare_rollout(self):
+ expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\n"
+ self.assert_execute_outputs(PrepareRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
+
+ def test_create_rollout(self):
+ expected_stderr = """Preparing rollout for bug 42.
+Updating working directory
+MOCK create_bug
+bug_title: REGRESSION(r852): Reason
+bug_description: http://trac.webkit.org/changeset/852 broke the build:
+Reason
+Running prepare-ChangeLog
+MOCK add_patch_to_bug: bug_id=None, description=ROLLOUT of r852, mark_for_review=False, mark_for_commit_queue=True, mark_for_landing=False
+-- Begin comment --
+Any committer can land this patch automatically by marking it commit-queue+. The commit-queue will build and test the patch before landing to ensure that the rollout will be successful. This process takes approximately 15 minutes.
+
+If you would like to land the rollout faster, you can use the following command:
+
+ webkit-patch land-attachment ATTACHMENT_ID --ignore-builders
+
+where ATTACHMENT_ID is the ID of this attachment.
+-- End comment --
+"""
+ self.assert_execute_outputs(CreateRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
+
def test_rollout(self):
- expected_stderr = "Updating working directory\nRunning prepare-ChangeLog\n\nNOTE: Rollout support is experimental.\nPlease verify the rollout diff and use \"webkit-patch land 12345\" to commit the rollout.\n"
+ expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\nMOCK: user.open_url: file://...\nBuilding WebKit\n"
self.assert_execute_outputs(Rollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
- def test_complete_rollout(self):
- options = self._default_options()
- options.complete_rollout = True
- expected_stderr = "Will re-open bug 12345 after rollout.\nUpdating working directory\nRunning prepare-ChangeLog\nBuilding WebKit\n"
- self.assert_execute_outputs(Rollout(), [852, "Reason"], options=options, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/commands/early_warning_system.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
index e3e14dd..9ea34c0 100644
--- a/WebKitTools/Scripts/webkitpy/commands/early_warning_system.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 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
@@ -29,11 +28,11 @@
from StringIO import StringIO
-from webkitpy.commands.queues import AbstractReviewQueue
-from webkitpy.committers import CommitterList
-from webkitpy.executive import ScriptError
-from webkitpy.webkitport import WebKitPort
-from webkitpy.queueengine import QueueEngine
+from webkitpy.tool.commands.queues import AbstractReviewQueue
+from webkitpy.common.config.committers import CommitterList
+from webkitpy.common.config.ports import WebKitPort
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.bot.queueengine import QueueEngine
class AbstractEarlyWarningSystem(AbstractReviewQueue):
@@ -44,30 +43,51 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue):
self.port = WebKitPort.port(self.port_name)
def should_proceed_with_work_item(self, patch):
+ return True
+
+ def _can_build(self):
try:
self.run_webkit_patch([
"build",
self.port.flag(),
+ "--build",
"--build-style=%s" % self._build_style,
"--force-clean",
+ "--no-update",
"--quiet"])
- self._update_status("Building", patch)
+ return True
except ScriptError, e:
self._update_status("Unable to perform a build")
return False
- return True
- def _review_patch(self, patch):
- self.run_webkit_patch([
- "build-attachment",
- self.port.flag(),
- "--build-style=%s" % self._build_style,
- "--force-clean",
- "--quiet",
- "--non-interactive",
- "--parent-command=%s" % self.name,
- "--no-update",
- patch.id()])
+ def _build(self, patch, first_run=False):
+ try:
+ args = [
+ "build-attachment",
+ self.port.flag(),
+ "--build",
+ "--build-style=%s" % self._build_style,
+ "--force-clean",
+ "--quiet",
+ "--non-interactive",
+ patch.id()]
+ if not first_run:
+ # See commit-queue for an explanation of what we're doing here.
+ args.append("--no-update")
+ args.append("--parent-command=%s" % self.name)
+ self.run_webkit_patch(args)
+ return True
+ except ScriptError, e:
+ if first_run:
+ return False
+ raise
+
+ def review_patch(self, patch):
+ if not self._build(patch, first_run=True):
+ if not self._can_build():
+ return False
+ self._build(patch)
+ return True
@classmethod
def handle_script_error(cls, tool, state, script_error):
@@ -95,14 +115,32 @@ class QtEWS(AbstractEarlyWarningSystem):
port_name = "qt"
-class ChromiumEWS(AbstractEarlyWarningSystem):
- name = "chromium-ews"
+class WinEWS(AbstractEarlyWarningSystem):
+ name = "win-ews"
+ port_name = "win"
+
+
+class AbstractChromiumEWS(AbstractEarlyWarningSystem):
port_name = "chromium"
watchers = AbstractEarlyWarningSystem.watchers + [
"dglazkov@chromium.org",
]
+class ChromiumLinuxEWS(AbstractChromiumEWS):
+ # FIXME: We should rename this command to cr-linux-ews, but that requires
+ # a database migration. :(
+ name = "chromium-ews"
+
+
+class ChromiumWindowsEWS(AbstractChromiumEWS):
+ name = "cr-win-ews"
+
+
+class ChromiumMacEWS(AbstractChromiumEWS):
+ name = "cr-mac-ews"
+
+
# For platforms that we can't run inside a VM (like Mac OS X), we require
# patches to be uploaded by committers, who are generally trustworthy folk. :)
class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem):
diff --git a/WebKitTools/Scripts/webkitpy/commands/early_warning_system_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
index d516b84..4d23a4c 100644
--- a/WebKitTools/Scripts/webkitpy/commands/early_warning_system_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
@@ -28,35 +28,48 @@
import os
-from webkitpy.commands.early_warning_system import *
-from webkitpy.commands.queuestest import QueuesTest
-from webkitpy.mock import Mock
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.earlywarningsystem import *
+from webkitpy.tool.commands.queuestest import QueuesTest
class EarlyWarningSytemTest(QueuesTest):
- def test_chromium_ews(self):
+ def test_failed_builds(self):
+ ews = ChromiumLinuxEWS()
+ ews._build = lambda patch, first_run=False: False
+ ews._can_build = lambda: True
+ ews.review_patch(Mock())
+
+ def test_chromium_linux_ews(self):
+ expected_stderr = {
+ "begin_work_queue": "CAUTION: chromium-ews will discard all local changes in \"%s\"\nRunning WebKit chromium-ews.\n" % os.getcwd(),
+ "handle_unexpected_error": "Mock error message\n",
+ }
+ self.assert_queue_outputs(ChromiumLinuxEWS(), expected_stderr=expected_stderr)
+
+ def test_chromium_windows_ews(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: chromium-ews will discard all local changes in \"%s\"\nRunning WebKit chromium-ews.\n" % os.getcwd(),
- "handle_unexpected_error" : "Mock error message\n",
+ "begin_work_queue": "CAUTION: cr-win-ews will discard all local changes in \"%s\"\nRunning WebKit cr-win-ews.\n" % os.getcwd(),
+ "handle_unexpected_error": "Mock error message\n",
}
- self.assert_queue_outputs(ChromiumEWS(), expected_stderr=expected_stderr)
+ self.assert_queue_outputs(ChromiumWindowsEWS(), expected_stderr=expected_stderr)
def test_qt_ews(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: qt-ews will discard all local changes in \"%s\"\nRunning WebKit qt-ews.\n" % os.getcwd(),
- "handle_unexpected_error" : "Mock error message\n",
+ "begin_work_queue": "CAUTION: qt-ews will discard all local changes in \"%s\"\nRunning WebKit qt-ews.\n" % os.getcwd(),
+ "handle_unexpected_error": "Mock error message\n",
}
self.assert_queue_outputs(QtEWS(), expected_stderr=expected_stderr)
def test_gtk_ews(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: gtk-ews will discard all local changes in \"%s\"\nRunning WebKit gtk-ews.\n" % os.getcwd(),
- "handle_unexpected_error" : "Mock error message\n",
+ "begin_work_queue": "CAUTION: gtk-ews will discard all local changes in \"%s\"\nRunning WebKit gtk-ews.\n" % os.getcwd(),
+ "handle_unexpected_error": "Mock error message\n",
}
self.assert_queue_outputs(GtkEWS(), expected_stderr=expected_stderr)
def test_mac_ews(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: mac-ews will discard all local changes in \"%s\"\nRunning WebKit mac-ews.\n" % os.getcwd(),
- "handle_unexpected_error" : "Mock error message\n",
+ "begin_work_queue": "CAUTION: mac-ews will discard all local changes in \"%s\"\nRunning WebKit mac-ews.\n" % os.getcwd(),
+ "handle_unexpected_error": "Mock error message\n",
}
self.assert_queue_outputs(MacEWS(), expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/commands/openbugs.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py
index 25bdefc..5da5bbb 100644
--- a/WebKitTools/Scripts/webkitpy/commands/openbugs.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py
@@ -29,8 +29,8 @@
import re
import sys
-from webkitpy.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.webkit_logging import log
+from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
+from webkitpy.common.system.deprecated_logging import log
class OpenBugs(AbstractDeclarativeCommand):
diff --git a/WebKitTools/Scripts/webkitpy/commands/openbugs_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py
index 71fefd2..40a6e1b 100644
--- a/WebKitTools/Scripts/webkitpy/commands/openbugs_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py
@@ -26,8 +26,8 @@
# (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 webkitpy.commands.commandtest import CommandsTest
-from webkitpy.commands.openbugs import OpenBugs
+from webkitpy.tool.commands.commandtest import CommandsTest
+from webkitpy.tool.commands.openbugs import OpenBugs
class OpenBugsTest(CommandsTest):
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py b/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py
new file mode 100644
index 0000000..e3fc00c
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2010 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 webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
+import webkitpy.tool.steps as steps
+
+
+class PrettyDiff(AbstractSequencedCommand):
+ name = "pretty-diff"
+ help_text = "Shows the pretty diff in the default browser"
+ steps = [
+ steps.ConfirmDiff,
+ ]
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py
new file mode 100644
index 0000000..645060c
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py
@@ -0,0 +1,285 @@
+# Copyright (c) 2009 Google Inc. All rights reserved.
+# 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:
+#
+# * 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 optparse import make_option
+
+from webkitpy.common.checkout.commitinfo import CommitInfo
+from webkitpy.common.config.committers import CommitterList
+from webkitpy.common.net.buildbot import BuildBot
+from webkitpy.common.system.user import User
+from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
+from webkitpy.common.system.deprecated_logging import log
+
+
+class BugsToCommit(AbstractDeclarativeCommand):
+ name = "bugs-to-commit"
+ help_text = "List bugs in the commit-queue"
+
+ def execute(self, options, args, tool):
+ # FIXME: This command is poorly named. It's fetching the commit-queue list here. The name implies it's fetching pending-commit (all r+'d patches).
+ bug_ids = tool.bugs.queries.fetch_bug_ids_from_commit_queue()
+ for bug_id in bug_ids:
+ print "%s" % bug_id
+
+
+class PatchesInCommitQueue(AbstractDeclarativeCommand):
+ name = "patches-in-commit-queue"
+ help_text = "List patches in the commit-queue"
+
+ def execute(self, options, args, tool):
+ patches = tool.bugs.queries.fetch_patches_from_commit_queue()
+ log("Patches in commit queue:")
+ for patch in patches:
+ print patch.url()
+
+
+class PatchesToCommitQueue(AbstractDeclarativeCommand):
+ name = "patches-to-commit-queue"
+ help_text = "Patches which should be added to the commit queue"
+ def __init__(self):
+ options = [
+ make_option("--bugs", action="store_true", dest="bugs", help="Output bug links instead of patch links"),
+ ]
+ AbstractDeclarativeCommand.__init__(self, options=options)
+
+ @staticmethod
+ def _needs_commit_queue(patch):
+ if patch.commit_queue() == "+": # If it's already cq+, ignore the patch.
+ log("%s already has cq=%s" % (patch.id(), patch.commit_queue()))
+ return False
+
+ # We only need to worry about patches from contributers who are not yet committers.
+ committer_record = CommitterList().committer_by_email(patch.attacher_email())
+ if committer_record:
+ log("%s committer = %s" % (patch.id(), committer_record))
+ return not committer_record
+
+ def execute(self, options, args, tool):
+ patches = tool.bugs.queries.fetch_patches_from_pending_commit_list()
+ patches_needing_cq = filter(self._needs_commit_queue, patches)
+ if options.bugs:
+ bugs_needing_cq = map(lambda patch: patch.bug_id(), patches_needing_cq)
+ bugs_needing_cq = sorted(set(bugs_needing_cq))
+ for bug_id in bugs_needing_cq:
+ print "%s" % tool.bugs.bug_url_for_bug_id(bug_id)
+ else:
+ for patch in patches_needing_cq:
+ print "%s" % tool.bugs.attachment_url_for_id(patch.id(), action="edit")
+
+
+class PatchesToReview(AbstractDeclarativeCommand):
+ name = "patches-to-review"
+ help_text = "List patches that are pending review"
+
+ def execute(self, options, args, tool):
+ patch_ids = tool.bugs.queries.fetch_attachment_ids_from_review_queue()
+ log("Patches pending review:")
+ for patch_id in patch_ids:
+ print patch_id
+
+
+class LastGreenRevision(AbstractDeclarativeCommand):
+ name = "last-green-revision"
+ help_text = "Prints the last known good revision"
+
+ def execute(self, options, args, tool):
+ print self.tool.buildbot.last_green_revision()
+
+
+class WhatBroke(AbstractDeclarativeCommand):
+ name = "what-broke"
+ help_text = "Print failing buildbots (%s) and what revisions broke them" % BuildBot.default_host
+
+ def _print_builder_line(self, builder_name, max_name_width, status_message):
+ print "%s : %s" % (builder_name.ljust(max_name_width), status_message)
+
+ # FIXME: This is slightly different from Builder.suspect_revisions_for_green_to_red_transition
+ # due to needing to detect the "hit the limit" case an print a special message.
+ def _print_blame_information_for_builder(self, builder_status, name_width, avoid_flakey_tests=True):
+ builder = self.tool.buildbot.builder_with_name(builder_status["name"])
+ red_build = builder.build(builder_status["build_number"])
+ (last_green_build, first_red_build) = builder.find_failure_transition(red_build)
+ if not first_red_build:
+ self._print_builder_line(builder.name(), name_width, "FAIL (error loading build information)")
+ return
+ if not last_green_build:
+ self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: sometime before %s?)" % first_red_build.revision())
+ return
+
+ suspect_revisions = range(first_red_build.revision(), last_green_build.revision(), -1)
+ suspect_revisions.reverse()
+ first_failure_message = ""
+ if (first_red_build == builder.build(builder_status["build_number"])):
+ first_failure_message = " FIRST FAILURE, possibly a flaky test"
+ self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: %s%s)" % (suspect_revisions, first_failure_message))
+ for revision in suspect_revisions:
+ commit_info = self.tool.checkout().commit_info_for_revision(revision)
+ if commit_info:
+ print commit_info.blame_string(self.tool.bugs)
+ else:
+ print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
+
+ def execute(self, options, args, tool):
+ builder_statuses = tool.buildbot.builder_statuses()
+ longest_builder_name = max(map(len, map(lambda builder: builder["name"], builder_statuses)))
+ failing_builders = 0
+ for builder_status in builder_statuses:
+ # If the builder is green, print OK, exit.
+ if builder_status["is_green"]:
+ continue
+ self._print_blame_information_for_builder(builder_status, name_width=longest_builder_name)
+ failing_builders += 1
+ if failing_builders:
+ print "%s of %s are failing" % (failing_builders, pluralize("builder", len(builder_statuses)))
+ else:
+ print "All builders are passing!"
+
+
+class WhoBrokeIt(AbstractDeclarativeCommand):
+ name = "who-broke-it"
+ help_text = "Print a list of revisions causing failures on %s" % BuildBot.default_host
+
+ def execute(self, options, args, tool):
+ for revision, builders in self.tool.buildbot.revisions_causing_failures(False).items():
+ print "r%s appears to have broken %s" % (revision, [builder.name() for builder in builders])
+
+
+class ResultsFor(AbstractDeclarativeCommand):
+ name = "results-for"
+ help_text = "Print a list of failures for the passed revision from bots on %s" % BuildBot.default_host
+ argument_names = "REVISION"
+
+ def _print_layout_test_results(self, results):
+ if not results:
+ print " No results."
+ return
+ for title, files in results.parsed_results().items():
+ print " %s" % title
+ for filename in files:
+ print " %s" % filename
+
+ def execute(self, options, args, tool):
+ builders = self.tool.buildbot.builders()
+ for builder in builders:
+ print "%s:" % builder.name()
+ build = builder.build_for_revision(args[0], allow_failed_lookups=True)
+ self._print_layout_test_results(build.layout_test_results())
+
+
+class FailureReason(AbstractDeclarativeCommand):
+ name = "failure-reason"
+ help_text = "Lists revisions where individual test failures started at %s" % BuildBot.default_host
+
+ def _print_blame_information_for_transition(self, green_build, red_build, failing_tests):
+ suspect_revisions = green_build.builder().suspect_revisions_for_transition(green_build, red_build)
+ print "SUCCESS: Build %s (r%s) was the first to show failures: %s" % (red_build._number, red_build.revision(), failing_tests)
+ print "Suspect revisions:"
+ for revision in suspect_revisions:
+ commit_info = self.tool.checkout().commit_info_for_revision(revision)
+ if commit_info:
+ print commit_info.blame_string(self.tool.bugs)
+ else:
+ print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
+
+ def _explain_failures_for_builder(self, builder, start_revision):
+ print "Examining failures for \"%s\", starting at r%s" % (builder.name(), start_revision)
+ revision_to_test = start_revision
+ build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True)
+ layout_test_results = build.layout_test_results()
+ if not layout_test_results:
+ # FIXME: This could be made more user friendly.
+ print "Failed to load layout test results; can't continue. (start revision = r%s)" % start_revision
+ return 1
+
+ results_to_explain = set(layout_test_results.failing_tests())
+ last_build_with_results = build
+ print "Starting at %s" % revision_to_test
+ while results_to_explain:
+ revision_to_test -= 1
+ new_build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True)
+ if not new_build:
+ print "No build for %s" % revision_to_test
+ continue
+ build = new_build
+ latest_results = build.layout_test_results()
+ if not latest_results:
+ print "No results build %s (r%s)" % (build._number, build.revision())
+ continue
+ failures = set(latest_results.failing_tests())
+ if len(failures) >= 20:
+ # FIXME: We may need to move this logic into the LayoutTestResults class.
+ # The buildbot stops runs after 20 failures so we don't have full results to work with here.
+ print "Too many failures in build %s (r%s), ignoring." % (build._number, build.revision())
+ continue
+ fixed_results = results_to_explain - failures
+ if not fixed_results:
+ print "No change in build %s (r%s), %s unexplained failures (%s in this build)" % (build._number, build.revision(), len(results_to_explain), len(failures))
+ last_build_with_results = build
+ continue
+ self._print_blame_information_for_transition(build, last_build_with_results, fixed_results)
+ last_build_with_results = build
+ results_to_explain -= fixed_results
+ if results_to_explain:
+ print "Failed to explain failures: %s" % results_to_explain
+ return 1
+ print "Explained all results for %s" % builder.name()
+ return 0
+
+ def _builder_to_explain(self):
+ builder_statuses = self.tool.buildbot.builder_statuses()
+ red_statuses = [status for status in builder_statuses if not status["is_green"]]
+ print "%s failing" % (pluralize("builder", len(red_statuses)))
+ builder_choices = [status["name"] for status in red_statuses]
+ # We could offer an "All" choice here.
+ chosen_name = User.prompt_with_list("Which builder to diagnose:", builder_choices)
+ # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object.
+ for status in red_statuses:
+ if status["name"] == chosen_name:
+ return (self.tool.buildbot.builder_with_name(chosen_name), status["built_revision"])
+
+ def execute(self, options, args, tool):
+ (builder, latest_revision) = self._builder_to_explain()
+ start_revision = self.tool.user.prompt("Revision to walk backwards from? [%s] " % latest_revision) or latest_revision
+ if not start_revision:
+ print "Revision required."
+ return 1
+ return self._explain_failures_for_builder(builder, start_revision=int(start_revision))
+
+class TreeStatus(AbstractDeclarativeCommand):
+ name = "tree-status"
+ help_text = "Print the status of the %s buildbots" % BuildBot.default_host
+ long_help = """Fetches build status from http://build.webkit.org/one_box_per_builder
+and displayes the status of each builder."""
+
+ def execute(self, options, args, tool):
+ for builder in tool.buildbot.builder_statuses():
+ status_string = "ok" if builder["is_green"] else "FAIL"
+ print "%s : %s" % (status_string.ljust(4), builder["name"])
diff --git a/WebKitTools/Scripts/webkitpy/commands/queries_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py
index b858777..98ed545 100644
--- a/WebKitTools/Scripts/webkitpy/commands/queries_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py
@@ -26,11 +26,11 @@
# (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 webkitpy.bugzilla import Bugzilla
-from webkitpy.commands.commandtest import CommandsTest
-from webkitpy.commands.queries import *
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
+from webkitpy.common.net.bugzilla import Bugzilla
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.commandtest import CommandsTest
+from webkitpy.tool.commands.queries import *
+from webkitpy.tool.mocktool import MockTool
class QueryCommandsTest(CommandsTest):
def test_bugs_to_commit(self):
diff --git a/WebKitTools/Scripts/webkitpy/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
index 6ea1c48..f0da379 100644
--- a/WebKitTools/Scripts/webkitpy/commands/queues.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -35,15 +34,15 @@ from datetime import datetime
from optparse import make_option
from StringIO import StringIO
-from webkitpy.bugzilla import CommitterValidator
-from webkitpy.executive import ScriptError
-from webkitpy.grammar import pluralize
-from webkitpy.webkit_logging import error, log
-from webkitpy.multicommandtool import Command
-from webkitpy.patchcollection import PersistentPatchCollection, PersistentPatchCollectionDelegate
-from webkitpy.statusserver import StatusServer
-from webkitpy.stepsequence import StepSequenceErrorHandler
-from webkitpy.queueengine import QueueEngine, QueueEngineDelegate
+from webkitpy.common.net.bugzilla import CommitterValidator
+from webkitpy.common.net.statusserver import StatusServer
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.common.system.deprecated_logging import error, log
+from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
+from webkitpy.tool.bot.patchcollection import PersistentPatchCollection, PersistentPatchCollectionDelegate
+from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
+from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.multicommandtool import Command
class AbstractQueue(Command, QueueEngineDelegate):
watchers = [
@@ -57,8 +56,10 @@ class AbstractQueue(Command, QueueEngineDelegate):
def __init__(self, options=None): # Default values should never be collections (like []) as default values are shared between invocations
options_list = (options or []) + [
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("--exit-after-iteration", action="store", type="int", dest="iterations", default=None, help="Stop running the queue after iterating this number of times."),
]
Command.__init__(self, "Run the %s" % self.name, options=options_list)
+ self._iteration_count = 0
def _cc_watchers(self, bug_id):
try:
@@ -67,24 +68,23 @@ class AbstractQueue(Command, QueueEngineDelegate):
traceback.print_exc()
log("Failed to CC watchers.")
- def _update_status(self, message, patch=None, results_file=None):
- self.tool.status_server.update_status(self.name, message, patch, results_file)
-
- def _did_pass(self, patch):
- self._update_status(self._pass_status, patch)
+ def run_webkit_patch(self, args):
+ webkit_patch_args = [self.tool.path()]
+ # FIXME: This is a hack, we should have a more general way to pass global options.
+ webkit_patch_args += ["--status-host=%s" % self.tool.status_server.host]
+ webkit_patch_args += map(str, args)
+ return self.tool.executive.run_and_throw_if_fail(webkit_patch_args)
- def _did_fail(self, patch):
- self._update_status(self._fail_status, patch)
+ def _log_directory(self):
+ return "%s-logs" % self.name
- def _did_error(self, patch, reason):
- message = "%s: %s" % (self._error_status, reason)
- self._update_status(message, patch)
+ # QueueEngineDelegate methods
def queue_log_path(self):
- return "%s.log" % self.name
+ return os.path.join(self._log_directory(), "%s.log" % self.name)
- def work_item_log_path(self, patch):
- return os.path.join("%s-logs" % self.name, "%s.log" % patch.bug_id())
+ def work_item_log_path(self, work_item):
+ raise NotImplementedError, "subclasses must implement"
def begin_work_queue(self):
log("CAUTION: %s will discard all local changes in \"%s\"" % (self.name, self.tool.scm().checkout_root))
@@ -95,7 +95,8 @@ class AbstractQueue(Command, QueueEngineDelegate):
log("Running WebKit %s." % self.name)
def should_continue_work_queue(self):
- return True
+ self._iteration_count += 1
+ return not self.options.iterations or self._iteration_count <= self.options.iterations
def next_work_item(self):
raise NotImplementedError, "subclasses must implement"
@@ -109,46 +110,62 @@ class AbstractQueue(Command, QueueEngineDelegate):
def handle_unexpected_error(self, work_item, message):
raise NotImplementedError, "subclasses must implement"
- def run_webkit_patch(self, args):
- webkit_patch_args = [self.tool.path()]
- # FIXME: This is a hack, we should have a more general way to pass global options.
- webkit_patch_args += ["--status-host=%s" % self.tool.status_server.host]
- webkit_patch_args += map(str, args)
- self.tool.executive.run_and_throw_if_fail(webkit_patch_args)
-
- def log_progress(self, patch_ids):
- log("%s in %s [%s]" % (pluralize("patch", len(patch_ids)), self.name, ", ".join(map(str, patch_ids))))
+ # Command methods
def execute(self, options, args, tool, engine=QueueEngine):
self.options = options
self.tool = tool
- return engine(self.name, self).run()
+ return engine(self.name, self, self.tool.wakeup_event).run()
@classmethod
def _update_status_for_script_error(cls, tool, state, script_error, is_error=False):
message = script_error.message
if is_error:
message = "Error: %s" % message
- output = script_error.message_with_output(output_limit=5*1024*1024) # 5MB
+ output = script_error.message_with_output(output_limit=1024*1024) # 1MB
return tool.status_server.update_status(cls.name, message, state["patch"], StringIO(output))
-class CommitQueue(AbstractQueue, StepSequenceErrorHandler):
+class AbstractPatchQueue(AbstractQueue):
+ def _update_status(self, message, patch=None, results_file=None):
+ self.tool.status_server.update_status(self.name, message, patch, results_file)
+
+ def _did_pass(self, patch):
+ self._update_status(self._pass_status, patch)
+
+ def _did_fail(self, patch):
+ self._update_status(self._fail_status, patch)
+
+ def _did_error(self, patch, reason):
+ message = "%s: %s" % (self._error_status, reason)
+ self._update_status(message, patch)
+
+ def work_item_log_path(self, patch):
+ return os.path.join(self._log_directory(), "%s.log" % patch.bug_id())
+
+ def log_progress(self, patch_ids):
+ log("%s in %s [%s]" % (pluralize("patch", len(patch_ids)), self.name, ", ".join(map(str, patch_ids))))
+
+
+class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler):
name = "commit-queue"
def __init__(self):
- AbstractQueue.__init__(self)
+ AbstractPatchQueue.__init__(self)
- # AbstractQueue methods
+ # AbstractPatchQueue methods
def begin_work_queue(self):
- AbstractQueue.begin_work_queue(self)
+ AbstractPatchQueue.begin_work_queue(self)
self.committer_validator = CommitterValidator(self.tool.bugs)
def _validate_patches_in_commit_queue(self):
# Not using BugzillaQueries.fetch_patches_from_commit_queue() so we can reject patches with invalid committers/reviewers.
bug_ids = self.tool.bugs.queries.fetch_bug_ids_from_commit_queue()
all_patches = sum([self.tool.bugs.fetch_bug(bug_id).commit_queued_patches(include_invalid=True) for bug_id in bug_ids], [])
- return self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches)
+ valid_patches = self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches)
+ if not self._builders_are_green():
+ return filter(lambda patch: patch.is_rollout(), valid_patches)
+ return valid_patches
def next_work_item(self):
patches = self._validate_patches_in_commit_queue()
@@ -162,9 +179,17 @@ class CommitQueue(AbstractQueue, StepSequenceErrorHandler):
def _can_build_and_test(self):
try:
- self.run_webkit_patch(["build-and-test", "--force-clean", "--non-interactive", "--build-style=both", "--quiet"])
+ self.run_webkit_patch([
+ "build-and-test",
+ "--force-clean",
+ "--build",
+ "--test",
+ "--non-interactive",
+ "--no-update",
+ "--build-style=both",
+ "--quiet"])
except ScriptError, e:
- self._update_status("Unabled to successfully build and test", None)
+ self._update_status("Unable to successfully build and test", None)
return False
return True
@@ -177,26 +202,67 @@ class CommitQueue(AbstractQueue, StepSequenceErrorHandler):
return True
def should_proceed_with_work_item(self, patch):
- if not self._builders_are_green():
- return False
- if not self._can_build_and_test():
- return False
- if not self._builders_are_green():
- return False
+ if not patch.is_rollout():
+ if not self._builders_are_green():
+ return False
self._update_status("Landing patch", patch)
return True
- def process_work_item(self, patch):
+ def _land(self, patch, first_run=False):
try:
- self._cc_watchers(patch.bug_id())
- # We pass --no-update here because we've already validated
- # that the current revision actually builds and passes the tests.
- # If we update, we risk moving to a revision that doesn't!
- self.run_webkit_patch(["land-attachment", "--force-clean", "--non-interactive", "--no-update", "--parent-command=commit-queue", "--build-style=both", "--quiet", patch.id()])
+ # We need to check the builders, unless we're trying to land a
+ # rollout (in which case the builders are probably red.)
+ if not patch.is_rollout() and not self._builders_are_green():
+ # We return true here because we want to return to the main
+ # QueueEngine loop as quickly as possible.
+ return True
+ args = [
+ "land-attachment",
+ "--force-clean",
+ "--build",
+ "--test",
+ "--non-interactive",
+ # The master process is responsible for checking the status
+ # of the builders (see above call to _builders_are_green).
+ "--ignore-builders",
+ "--build-style=both",
+ "--quiet",
+ patch.id()
+ ]
+ if not first_run:
+ # The first time through, we don't reject the patch from the
+ # commit queue because we want to make sure we can build and
+ # test ourselves. However, the second time through, we
+ # register ourselves as the parent-command so we can reject
+ # the patch on failure.
+ args.append("--parent-command=commit-queue")
+ # The second time through, we also don't want to update so we
+ # know we're testing the same revision that we successfully
+ # built and tested.
+ args.append("--no-update")
+ self.run_webkit_patch(args)
self._did_pass(patch)
+ return True
except ScriptError, e:
+ if first_run:
+ return False
self._did_fail(patch)
- raise e
+ raise
+
+ def process_work_item(self, patch):
+ self._cc_watchers(patch.bug_id())
+ if not self._land(patch, first_run=True):
+ # The patch failed to land, but the bots were green. It's possible
+ # that the bots were behind. To check that case, we try to build and
+ # test ourselves.
+ if not self._can_build_and_test():
+ return False
+ # Hum, looks like the patch is actually bad. Of course, we could
+ # have been bitten by a flaky test the first time around. We try
+ # to land again. If it fails a second time, we're pretty sure its
+ # a bad test and re can reject it outright.
+ self._land(patch)
+ return True
def handle_unexpected_error(self, patch, message):
self.committer_validator.reject_patch_from_commit_queue(patch.id(), message)
@@ -217,11 +283,11 @@ class CommitQueue(AbstractQueue, StepSequenceErrorHandler):
validator.reject_patch_from_commit_queue(state["patch"].id(), cls._error_message_for_bug(tool, status_id, script_error))
-class AbstractReviewQueue(AbstractQueue, PersistentPatchCollectionDelegate, StepSequenceErrorHandler):
+class AbstractReviewQueue(AbstractPatchQueue, PersistentPatchCollectionDelegate, StepSequenceErrorHandler):
def __init__(self, options=None):
- AbstractQueue.__init__(self, options)
+ AbstractPatchQueue.__init__(self, options)
- def _review_patch(self, patch):
+ def review_patch(self, patch):
raise NotImplementedError, "subclasses must implement"
# PersistentPatchCollectionDelegate methods
@@ -238,10 +304,10 @@ class AbstractReviewQueue(AbstractQueue, PersistentPatchCollectionDelegate, Step
def is_terminal_status(self, status):
return status == "Pass" or status == "Fail" or status.startswith("Error:")
- # AbstractQueue methods
+ # AbstractPatchQueue methods
def begin_work_queue(self):
- AbstractQueue.begin_work_queue(self)
+ AbstractPatchQueue.begin_work_queue(self)
self._patches = PersistentPatchCollection(self)
def next_work_item(self):
@@ -255,8 +321,10 @@ class AbstractReviewQueue(AbstractQueue, PersistentPatchCollectionDelegate, Step
def process_work_item(self, patch):
try:
- self._review_patch(patch)
+ if not self.review_patch(patch):
+ return False
self._did_pass(patch)
+ return True
except ScriptError, e:
if e.exit_code != QueueEngine.handled_error_code:
self._did_fail(patch)
@@ -281,8 +349,9 @@ class StyleQueue(AbstractReviewQueue):
self._update_status("Checking style", patch)
return True
- def _review_patch(self, patch):
+ def review_patch(self, patch):
self.run_webkit_patch(["check-style", "--force-clean", "--non-interactive", "--parent-command=style-queue", patch.id()])
+ return True
@classmethod
def handle_script_error(cls, tool, state, script_error):
diff --git a/WebKitTools/Scripts/webkitpy/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
index 87cd645..f0f7c86 100644
--- a/WebKitTools/Scripts/webkitpy/commands/queues_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
@@ -28,14 +28,16 @@
import os
-from webkitpy.commands.commandtest import CommandsTest
-from webkitpy.commands.queues import *
-from webkitpy.commands.queuestest import QueuesTest
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
+from webkitpy.common.net.bugzilla import Attachment
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.commandtest import CommandsTest
+from webkitpy.tool.commands.queues import *
+from webkitpy.tool.commands.queuestest import QueuesTest
+from webkitpy.tool.mocktool import MockTool, MockSCM
-class TestQueue(AbstractQueue):
+class TestQueue(AbstractPatchQueue):
name = "test-queue"
@@ -43,6 +45,17 @@ class TestReviewQueue(AbstractReviewQueue):
name = "test-review-queue"
+class MockPatch(object):
+ def is_rollout(self):
+ return True
+
+ def bug_id(self):
+ return 12345
+
+ def id(self):
+ return 76543
+
+
class AbstractQueueTest(CommandsTest):
def _assert_log_progress_output(self, patch_ids, progress_output):
OutputCapture().assert_outputs(self, TestQueue().log_progress, [patch_ids], expected_stderr=progress_output)
@@ -52,9 +65,13 @@ class AbstractQueueTest(CommandsTest):
self._assert_log_progress_output(["1","2","3"], "3 patches in test-queue [1, 2, 3]\n")
self._assert_log_progress_output([1], "1 patch in test-queue [1]\n")
+ def test_log_directory(self):
+ self.assertEquals(TestQueue()._log_directory(), "test-queue-logs")
+
def _assert_run_webkit_patch(self, run_args):
queue = TestQueue()
- tool = MockBugzillaTool()
+ tool = MockTool()
+ tool.executive = Mock()
queue.bind_to_tool(tool)
queue.run_webkit_patch(run_args)
@@ -65,11 +82,28 @@ class AbstractQueueTest(CommandsTest):
self._assert_run_webkit_patch([1])
self._assert_run_webkit_patch(["one", 2])
+ def test_iteration_count(self):
+ queue = TestQueue()
+ queue.options = Mock()
+ queue.options.iterations = 3
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertFalse(queue.should_continue_work_queue())
+
+ def test_no_iteration_count(self):
+ queue = TestQueue()
+ queue.options = Mock()
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertTrue(queue.should_continue_work_queue())
+ self.assertTrue(queue.should_continue_work_queue())
+
class AbstractReviewQueueTest(CommandsTest):
def test_patch_collection_delegate_methods(self):
queue = TestReviewQueue()
- tool = MockBugzillaTool()
+ tool = MockTool()
queue.bind_to_tool(tool)
self.assertEquals(queue.collection_name(), "test-review-queue")
self.assertEquals(queue.fetch_potential_patch_ids(), [103])
@@ -83,7 +117,7 @@ class AbstractReviewQueueTest(CommandsTest):
class CommitQueueTest(QueuesTest):
def test_commit_queue(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % os.getcwd(),
+ "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
# FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
"next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
@@ -92,11 +126,39 @@ Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.c
}
self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr)
+ def test_rollout(self):
+ tool = MockTool(log_executive=True)
+ tool.buildbot.light_tree_on_fire()
+ expected_stderr = {
+ "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
+ # FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
+ "next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+1 patch in commit-queue [106]
+""",
+ }
+ self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
+
+ def test_rollout_lands(self):
+ tool = MockTool(log_executive=True)
+ tool.buildbot.light_tree_on_fire()
+ rollout_patch = MockPatch()
+ expected_stderr = {
+ "begin_work_queue": "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
+ # FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
+ "next_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+1 patch in commit-queue [106]
+""",
+ "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--build', '--test', '--non-interactive', '--ignore-builders', '--build-style=both', '--quiet', '76543']\n",
+ }
+ self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
+
class StyleQueueTest(QueuesTest):
def test_style_queue(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: style-queue will discard all local changes in \"%s\"\nRunning WebKit style-queue.\n" % os.getcwd(),
+ "begin_work_queue" : "CAUTION: style-queue will discard all local changes in \"%s\"\nRunning WebKit style-queue.\n" % MockSCM.fake_checkout_root,
"handle_unexpected_error" : "Mock error message\n",
}
self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/commands/queuestest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
index 09d1c26..bf7e32a 100644
--- a/WebKitTools/Scripts/webkitpy/commands/queuestest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
@@ -28,14 +28,14 @@
import unittest
-from webkitpy.bugzilla import Attachment
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
+from webkitpy.common.net.bugzilla import Attachment
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.mocktool import MockTool
class MockQueueEngine(object):
- def __init__(self, name, queue):
+ def __init__(self, name, queue, wakeup_event):
pass
def run(self):
@@ -44,12 +44,13 @@ class MockQueueEngine(object):
class QueuesTest(unittest.TestCase):
mock_work_item = Attachment({
- "id" : 1234,
- "bug_id" : 345,
+ "id": 1234,
+ "bug_id": 345,
+ "name": "Patch",
"attacher_email": "adam@example.com",
}, None)
- def assert_queue_outputs(self, queue, args=None, work_item=None, expected_stdout=None, expected_stderr=None, options=Mock(), tool=MockBugzillaTool()):
+ def assert_queue_outputs(self, queue, args=None, work_item=None, expected_stdout=None, expected_stderr=None, options=Mock(), tool=MockTool()):
if not expected_stdout:
expected_stdout = {}
if not expected_stderr:
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py
new file mode 100644
index 0000000..eb80d8f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py
@@ -0,0 +1,107 @@
+# Copyright (c) 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.
+
+import os
+
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.config.ports import WebKitPort
+from webkitpy.tool.bot.sheriff import Sheriff
+from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot
+from webkitpy.tool.commands.queues import AbstractQueue
+from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
+
+
+class SheriffBot(AbstractQueue, StepSequenceErrorHandler):
+ name = "sheriff-bot"
+ watchers = AbstractQueue.watchers + [
+ "abarth@webkit.org",
+ "eric@webkit.org",
+ ]
+
+ def _update(self):
+ self.run_webkit_patch(["update", "--force-clean", "--quiet"])
+
+ # AbstractQueue methods
+
+ def begin_work_queue(self):
+ AbstractQueue.begin_work_queue(self)
+ self._sheriff = Sheriff(self.tool, self)
+ self._irc_bot = SheriffIRCBot(self.tool, self._sheriff)
+ self.tool.ensure_irc_connected(self._irc_bot.irc_delegate())
+
+ def work_item_log_path(self, new_failures):
+ return os.path.join("%s-logs" % self.name, "%s.log" % new_failures.keys()[0])
+
+ def next_work_item(self):
+ self._irc_bot.process_pending_messages()
+ self._update()
+ new_failures = {}
+ revisions_causing_failures = self.tool.buildbot.revisions_causing_failures()
+ for svn_revision, builders in revisions_causing_failures.items():
+ if self.tool.status_server.svn_revision(svn_revision):
+ # FIXME: We should re-process the work item after some time delay.
+ # https://bugs.webkit.org/show_bug.cgi?id=36581
+ continue
+ new_failures[svn_revision] = builders
+ self._sheriff.provoke_flaky_builders(revisions_causing_failures)
+ return new_failures
+
+ def should_proceed_with_work_item(self, new_failures):
+ # Currently, we don't have any reasons not to proceed with work items.
+ return True
+
+ def process_work_item(self, new_failures):
+ blame_list = new_failures.keys()
+ for svn_revision, builders in new_failures.items():
+ try:
+ commit_info = self.tool.checkout().commit_info_for_revision(svn_revision)
+ if not commit_info:
+ print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
+ continue
+ self._sheriff.post_irc_warning(commit_info, builders)
+ self._sheriff.post_blame_comment_on_bug(commit_info,
+ builders,
+ blame_list)
+ self._sheriff.post_automatic_rollout_patch(commit_info,
+ builders)
+ finally:
+ for builder in builders:
+ self.tool.status_server.update_svn_revision(svn_revision,
+ builder.name())
+ return True
+
+ def handle_unexpected_error(self, new_failures, message):
+ log(message)
+
+ # StepSequenceErrorHandler methods
+
+ @classmethod
+ def handle_script_error(cls, tool, state, script_error):
+ # Ideally we would post some information to IRC about what went wrong
+ # here, but we don't have the IRC password in the child process.
+ pass
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
new file mode 100644
index 0000000..f121eda
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from webkitpy.tool.commands.queuestest import QueuesTest
+from webkitpy.tool.commands.sheriffbot import SheriffBot
+from webkitpy.tool.mocktool import mock_builder
+
+
+class SheriffBotTest(QueuesTest):
+ def test_sheriff_bot(self):
+ mock_work_item = {
+ 29837: [mock_builder],
+ }
+ expected_stderr = {
+ "begin_work_queue": "CAUTION: sheriff-bot will discard all local changes in \"%s\"\nRunning WebKit sheriff-bot.\n" % os.getcwd(),
+ "next_work_item": "",
+ "process_work_item": "MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Mock builder name (Tests)\nMOCK bug comment: bug_id=42, cc=['webkit-bot-watchers@googlegroups.com', 'abarth@webkit.org', 'eric@webkit.org']\n--- Begin comment ---\\http://trac.webkit.org/changeset/29837 might have broken Mock builder name (Tests)\n--- End comment ---\n\n",
+ "handle_unexpected_error": "Mock error message\n"
+ }
+ self.assert_queue_outputs(SheriffBot(), work_item=mock_work_item, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/stepsequence.py b/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py
index 008b366..c6de79f 100644
--- a/WebKitTools/Scripts/webkitpy/stepsequence.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py
@@ -26,12 +26,12 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import webkitpy.steps as steps
+import webkitpy.tool.steps as steps
-from webkitpy.executive import ScriptError
-from webkitpy.webkit_logging import log
-from webkitpy.scm import CheckoutNeedsUpdate
-from webkitpy.queueengine import QueueEngine
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
+from webkitpy.tool.bot.queueengine import QueueEngine
+from webkitpy.common.system.deprecated_logging import log
class StepSequenceErrorHandler():
@@ -66,7 +66,6 @@ class StepSequence(object):
self._run(tool, options, state)
except CheckoutNeedsUpdate, e:
log("Commit failed 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.")
QueueEngine.exit_after_handled_error(e)
except ScriptError, e:
if not options.quiet:
diff --git a/WebKitTools/Scripts/webkitpy/commands/upload.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
index 15bdfbb..bdf060a 100644
--- a/WebKitTools/Scripts/webkitpy/commands/upload.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009, 2010 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -35,25 +35,26 @@ import sys
from optparse import make_option
-import webkitpy.steps as steps
+import webkitpy.tool.steps as steps
-from webkitpy.bugzilla import parse_bug_id
-from webkitpy.commands.abstractsequencedcommand import AbstractSequencedCommand
-from webkitpy.comments import bug_comment_from_svn_revision
-from webkitpy.committers import CommitterList
-from webkitpy.grammar import pluralize, join_with_separators
-from webkitpy.webkit_logging import error, log
-from webkitpy.mock import Mock
-from webkitpy.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.user import User
+from webkitpy.common.config.committers import CommitterList
+from webkitpy.common.net.bugzilla import parse_bug_id
+from webkitpy.common.system.user import User
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
+from webkitpy.tool.grammar import pluralize, join_with_separators
+from webkitpy.tool.comments import bug_comment_from_svn_revision
+from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
+from webkitpy.common.system.deprecated_logging import error, log
class CommitMessageForCurrentDiff(AbstractDeclarativeCommand):
name = "commit-message"
help_text = "Print a commit message suitable for the uncommitted changes"
def execute(self, options, args, tool):
- os.chdir(tool.scm().checkout_root)
- print "%s" % tool.scm().commit_message_for_this_commit().message()
+ # This command is a useful test to make sure commit_message_for_this_commit
+ # always returns the right value regardless of the current working directory.
+ print "%s" % tool.checkout().commit_message_for_this_commit().message()
class CleanPendingCommit(AbstractDeclarativeCommand):
name = "clean-pending-commit"
@@ -97,8 +98,8 @@ class AssignToCommitter(AbstractDeclarativeCommand):
def _assign_bug_to_last_patch_attacher(self, bug_id):
committers = CommitterList()
bug = self.tool.bugs.fetch_bug(bug_id)
- assigned_to_email = bug.assigned_to_email()
- if assigned_to_email != self.tool.bugs.unassigned_email:
+ if not bug.is_unassigned():
+ assigned_to_email = bug.assigned_to_email()
log("Bug %s is already assigned to %s (%s)." % (bug_id, assigned_to_email, committers.committer_by_email(assigned_to_email)))
return
@@ -144,15 +145,14 @@ class AbstractPatchUploadingCommand(AbstractSequencedCommand):
# Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs).
bug_id = args and args[0]
if not bug_id:
- state["diff"] = tool.scm().create_patch()
- bug_id = parse_bug_id(state["diff"])
+ bug_id = tool.checkout().bug_id_for_this_commit()
return bug_id
def _prepare_state(self, options, args, tool):
state = {}
state["bug_id"] = self._bug_id(args, tool, state)
if not state["bug_id"]:
- error("No bug id passed and no bug url found in diff.")
+ error("No bug id passed and no bug url found in ChangeLogs.")
return state
@@ -164,6 +164,7 @@ class Post(AbstractPatchUploadingCommand):
steps = [
steps.CheckStyle,
steps.ConfirmDiff,
+ steps.PostCodeReview,
steps.ObsoletePatches,
steps.PostDiff,
]
@@ -208,6 +209,7 @@ class Upload(AbstractPatchUploadingCommand):
steps.PrepareChangeLog,
steps.EditChangeLog,
steps.ConfirmDiff,
+ steps.PostCodeReview,
steps.ObsoletePatches,
steps.PostDiff,
]
@@ -288,6 +290,7 @@ class PostCommits(AbstractDeclarativeCommand):
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)
+# FIXME: This command needs to be brought into the modern age with steps and CommitInfo.
class MarkBugFixed(AbstractDeclarativeCommand):
name = "mark-bug-fixed"
help_text = "Mark the specified bug as fixed"
@@ -301,6 +304,7 @@ class MarkBugFixed(AbstractDeclarativeCommand):
]
AbstractDeclarativeCommand.__init__(self, options=options)
+ # FIXME: We should be using checkout().changelog_entries_for_revision(...) instead here.
def _fetch_commit_log(self, tool, svn_revision):
if not svn_revision:
return tool.scm().last_svn_commit_log()
@@ -415,7 +419,7 @@ class CreateBug(AbstractDeclarativeCommand):
if options.prompt:
(bug_title, comment_text) = self.prompt_for_bug_title_and_comment()
else:
- commit_message = tool.scm().commit_message_for_this_commit()
+ commit_message = tool.checkout().commit_message_for_this_commit()
bug_title = commit_message.description(lstrip=True, strip_url=True)
comment_text = commit_message.body(lstrip=True)
diff --git a/WebKitTools/Scripts/webkitpy/commands/upload_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
index 7fa8797..271df01 100644
--- a/WebKitTools/Scripts/webkitpy/commands/upload_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
@@ -26,17 +26,17 @@
# (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 webkitpy.commands.commandtest import CommandsTest
-from webkitpy.commands.upload import *
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.commands.commandtest import CommandsTest
+from webkitpy.tool.commands.upload import *
+from webkitpy.tool.mocktool import MockTool
class UploadCommandsTest(CommandsTest):
def test_commit_message_for_current_diff(self):
- tool = MockBugzillaTool()
+ tool = MockTool()
mock_commit_message_for_this_commit = Mock()
mock_commit_message_for_this_commit.message = lambda: "Mock message"
- tool._scm.commit_message_for_this_commit = lambda: mock_commit_message_for_this_commit
+ tool._checkout.commit_message_for_this_commit = lambda: mock_commit_message_for_this_commit
expected_stdout = "Mock message\n"
self.assert_execute_outputs(CommitMessageForCurrentDiff(), [], expected_stdout=expected_stdout, tool=tool)
@@ -44,7 +44,7 @@ class UploadCommandsTest(CommandsTest):
self.assert_execute_outputs(CleanPendingCommit(), [])
def test_assign_to_committer(self):
- tool = MockBugzillaTool()
+ tool = MockTool()
expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\nBug 77 is already assigned to foo@foo.com (None).\nBug 76 has no non-obsolete patches, ignoring.\n"
self.assert_execute_outputs(AssignToCommitter(), [], expected_stderr=expected_stderr, tool=tool)
tool.bugs.reassign_bug.assert_called_with(42, "eric@webkit.org", "Attachment 128 was posted by a committer and has review+, assigning to Eric Seidel for commit.")
@@ -54,33 +54,57 @@ class UploadCommandsTest(CommandsTest):
self.assert_execute_outputs(ObsoleteAttachments(), [42], expected_stderr=expected_stderr)
def test_post(self):
- expected_stderr = "Running check-webkit-style\nObsoleting 2 old patches on bug 42\n"
- self.assert_execute_outputs(Post(), [42], expected_stderr=expected_stderr)
+ options = Mock()
+ options.description = "MOCK description"
+ options.request_commit = False
+ options.review = True
+ options.cc = None
+ expected_stderr = """Running check-webkit-style
+MOCK: user.open_url: file://...
+Obsoleting 2 old patches on bug 42
+MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False
+-- Begin comment --
+None
+-- End comment --
+MOCK: user.open_url: http://example.com/42
+"""
+ self.assert_execute_outputs(Post(), [42], options=options, expected_stderr=expected_stderr)
- def test_post(self):
- expected_stderr = "Obsoleting 2 old patches on bug 42\n"
+ def test_land_safely(self):
+ expected_stderr = "Obsoleting 2 old patches on bug 42\nMOCK add_patch_to_bug: bug_id=42, description=Patch for landing, mark_for_review=False, mark_for_commit_queue=False, mark_for_landing=True\n-- Begin comment --\nNone\n-- End comment --\n"
self.assert_execute_outputs(LandSafely(), [42], expected_stderr=expected_stderr)
def test_prepare_diff_with_arg(self):
self.assert_execute_outputs(Prepare(), [42])
def test_prepare(self):
- self.assert_execute_outputs(Prepare(), [])
+ expected_stderr = "MOCK create_bug\nbug_title: Mock user response\nbug_description: Mock user response\n"
+ self.assert_execute_outputs(Prepare(), [], expected_stderr=expected_stderr)
def test_upload(self):
- expected_stderr = "Running check-webkit-style\nObsoleting 2 old patches on bug 42\nMOCK: user.open_url: http://example.com/42\n"
- self.assert_execute_outputs(Upload(), [42], expected_stderr=expected_stderr)
+ options = Mock()
+ options.description = "MOCK description"
+ options.request_commit = False
+ options.review = True
+ options.cc = None
+ expected_stderr = """Running check-webkit-style
+MOCK: user.open_url: file://...
+Obsoleting 2 old patches on bug 42
+MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False
+-- Begin comment --
+None
+-- End comment --
+MOCK: user.open_url: http://example.com/42
+"""
+ self.assert_execute_outputs(Upload(), [42], options=options, expected_stderr=expected_stderr)
def test_mark_bug_fixed(self):
- tool = MockBugzillaTool()
+ tool = MockTool()
tool._scm.last_svn_commit_log = lambda: "r9876 |"
options = Mock()
options.bug_id = 42
- expected_stderr = """Bug: <http://example.com/42> Bug with two r+'d and cq+'d patches, one of which has an invalid commit-queue setter.
-Revision: 9876
-MOCK: user.open_url: http://example.com/42
-Adding comment to Bug 42.
-"""
+ options.comment = "MOCK comment"
+ expected_stderr = "Bug: <http://example.com/42> Bug with two r+'d and cq+'d patches, one of which has an invalid commit-queue setter.\nRevision: 9876\nMOCK: user.open_url: http://example.com/42\nAdding comment to Bug 42.\nMOCK bug comment: bug_id=42, cc=None\n--- Begin comment ---\\MOCK comment\n\nCommitted r9876: <http://trac.webkit.org/changeset/9876>\n--- End comment ---\n\n"
self.assert_execute_outputs(MarkBugFixed(), [], expected_stderr=expected_stderr, tool=tool, options=options)
def test_edit_changelog(self):
diff --git a/WebKitTools/Scripts/webkitpy/comments.py b/WebKitTools/Scripts/webkitpy/tool/comments.py
index 77ad239..83f2be8 100755
--- a/WebKitTools/Scripts/webkitpy/comments.py
+++ b/WebKitTools/Scripts/webkitpy/tool/comments.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
# A tool for automating dealing with bugzilla, posting patches, committing
# patches, etc.
-from webkitpy.changelogs import view_source_url
+from webkitpy.common.checkout.changelog import view_source_url
def bug_comment_from_svn_revision(svn_revision):
diff --git a/WebKitTools/Scripts/webkitpy/grammar.py b/WebKitTools/Scripts/webkitpy/tool/grammar.py
index 651bbc9..8db9826 100644
--- a/WebKitTools/Scripts/webkitpy/grammar.py
+++ b/WebKitTools/Scripts/webkitpy/tool/grammar.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -45,9 +44,11 @@ def pluralize(noun, count):
return "%d %s" % (count, noun)
-def join_with_separators(list_of_strings, separator=', ', last_separator=', and '):
+def join_with_separators(list_of_strings, separator=', ', only_two_separator=" and ", last_separator=', and '):
if not list_of_strings:
return ""
if len(list_of_strings) == 1:
return list_of_strings[0]
+ if len(list_of_strings) == 2:
+ return only_two_separator.join(list_of_strings)
return "%s%s%s" % (separator.join(list_of_strings[:-1]), last_separator, list_of_strings[-1])
diff --git a/WebKitTools/Scripts/webkitpy/grammar_unittest.py b/WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py
index 3d8b179..cab71db 100644
--- a/WebKitTools/Scripts/webkitpy/grammar_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py
@@ -27,11 +27,14 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
-from webkitpy.grammar import join_with_separators
+
+from webkitpy.tool.grammar import join_with_separators
class GrammarTest(unittest.TestCase):
def test_join_with_separators(self):
+ self.assertEqual(join_with_separators(["one"]), "one")
+ self.assertEqual(join_with_separators(["one", "two"]), "one and two")
self.assertEqual(join_with_separators(["one", "two", "three"]), "one, two, and three")
if __name__ == '__main__':
diff --git a/WebKitTools/Scripts/webkitpy/tool/main.py b/WebKitTools/Scripts/webkitpy/tool/main.py
new file mode 100755
index 0000000..06cde74
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/main.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2010 Google Inc. All rights reserved.
+# 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:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# A tool for automating dealing with bugzilla, posting patches, committing patches, etc.
+
+import os
+import threading
+
+from webkitpy.common.checkout.api import Checkout
+from webkitpy.common.checkout.scm import detect_scm_system
+from webkitpy.common.net.bugzilla import Bugzilla
+from webkitpy.common.net.buildbot import BuildBot
+from webkitpy.common.net.rietveld import Rietveld
+from webkitpy.common.net.irc.ircproxy import IRCProxy
+from webkitpy.common.system.executive import Executive
+from webkitpy.common.system.user import User
+import webkitpy.tool.commands as commands
+# FIXME: Remove these imports once all the commands are in the root of the
+# command package.
+from webkitpy.tool.commands.download import *
+from webkitpy.tool.commands.earlywarningsystem import *
+from webkitpy.tool.commands.openbugs import OpenBugs
+from webkitpy.tool.commands.queries import *
+from webkitpy.tool.commands.queues import *
+from webkitpy.tool.commands.sheriffbot import *
+from webkitpy.tool.commands.upload import *
+from webkitpy.tool.multicommandtool import MultiCommandTool
+from webkitpy.common.system.deprecated_logging import log
+
+
+class WebKitPatch(MultiCommandTool):
+ global_options = [
+ make_option("--dry-run", action="store_true", dest="dry_run", default=False, help="do not touch remote servers"),
+ make_option("--status-host", action="store", dest="status_host", type="string", nargs=1, help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted."),
+ make_option("--irc-password", action="store", dest="irc_password", type="string", nargs=1, help="Password to use when communicating via IRC."),
+ ]
+
+ def __init__(self, path):
+ MultiCommandTool.__init__(self)
+
+ self._path = path
+ self.wakeup_event = threading.Event()
+ self.bugs = Bugzilla()
+ self.buildbot = BuildBot()
+ self.executive = Executive()
+ self._irc = None
+ self.user = User()
+ self._scm = None
+ self._checkout = None
+ self.status_server = StatusServer()
+ self.codereview = Rietveld(self.executive)
+
+ def scm(self):
+ # Lazily initialize SCM to not error-out before command line parsing (or when running non-scm commands).
+ original_cwd = os.path.abspath(".")
+ if not self._scm:
+ self._scm = detect_scm_system(original_cwd)
+
+ if not self._scm:
+ script_directory = os.path.abspath(sys.path[0])
+ self._scm = detect_scm_system(script_directory)
+ if self._scm:
+ log("The current directory (%s) is not a WebKit checkout, using %s" % (original_cwd, self._scm.checkout_root))
+ else:
+ error("FATAL: Failed to determine the SCM system for either %s or %s" % (original_cwd, script_directory))
+
+ return self._scm
+
+ def checkout(self):
+ if not self._checkout:
+ self._checkout = Checkout(self.scm())
+ return self._checkout
+
+ def ensure_irc_connected(self, irc_delegate):
+ if not self._irc:
+ self._irc = IRCProxy(irc_delegate)
+
+ def irc(self):
+ # We don't automatically construct IRCProxy here because constructing
+ # IRCProxy actually connects to IRC. We want clients to explicitly
+ # connect to IRC.
+ return self._irc
+
+ def path(self):
+ return self._path
+
+ def command_completed(self):
+ if self._irc:
+ self._irc.disconnect()
+
+ def should_show_in_main_help(self, command):
+ if not command.show_in_main_help:
+ return False
+ if command.requires_local_commits:
+ return self.scm().supports_local_commits()
+ return True
+
+ # FIXME: This may be unnecessary since we pass global options to all commands during execute() as well.
+ def handle_global_options(self, options):
+ if options.dry_run:
+ self.scm().dryrun = True
+ self.bugs.dryrun = True
+ self.codereview.dryrun = True
+ if options.status_host:
+ self.status_server.set_host(options.status_host)
+ if options.irc_password:
+ self.irc_password = options.irc_password
+
+ def should_execute_command(self, command):
+ if command.requires_local_commits and not self.scm().supports_local_commits():
+ failure_reason = "%s requires local commits using %s in %s." % (command.name, self.scm().display_name(), self.scm().checkout_root)
+ return (False, failure_reason)
+ return (True, None)
diff --git a/WebKitTools/Scripts/webkitpy/mock_bugzillatool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
index f522e40..cc361ff 100644
--- a/WebKitTools/Scripts/webkitpy/mock_bugzillatool.py
+++ b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
@@ -27,12 +27,15 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
+import threading
-from webkitpy.bugzilla import Bug, Attachment
-from webkitpy.committers import CommitterList, Reviewer
-from webkitpy.mock import Mock
-from webkitpy.scm import CommitMessage
-from webkitpy.webkit_logging import log
+from webkitpy.common.config.committers import CommitterList, Reviewer
+from webkitpy.common.checkout.commitinfo import CommitInfo
+from webkitpy.common.checkout.scm import CommitMessage
+from webkitpy.common.net.bugzilla import Bug, Attachment
+from webkitpy.common.net.rietveld import Rietveld
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.common.system.deprecated_logging import log
def _id_to_object_dictionary(*objects):
@@ -41,6 +44,7 @@ def _id_to_object_dictionary(*objects):
dictionary[thing["id"]] = thing
return dictionary
+# Testing
# FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
@@ -49,6 +53,7 @@ _patch1 = {
"id": 197,
"bug_id": 42,
"url": "http://example.com/197",
+ "name": "Patch1",
"is_obsolete": False,
"is_patch": True,
"review": "+",
@@ -63,6 +68,7 @@ _patch2 = {
"id": 128,
"bug_id": 42,
"url": "http://example.com/128",
+ "name": "Patch2",
"is_obsolete": False,
"is_patch": True,
"review": "+",
@@ -77,6 +83,7 @@ _patch3 = {
"id": 103,
"bug_id": 75,
"url": "http://example.com/103",
+ "name": "Patch3",
"is_obsolete": False,
"is_patch": True,
"review": "?",
@@ -88,6 +95,7 @@ _patch4 = {
"id": 104,
"bug_id": 77,
"url": "http://example.com/103",
+ "name": "Patch3",
"is_obsolete": False,
"is_patch": True,
"review": "+",
@@ -101,6 +109,7 @@ _patch5 = {
"id": 105,
"bug_id": 77,
"url": "http://example.com/103",
+ "name": "Patch5",
"is_obsolete": False,
"is_patch": True,
"review": "+",
@@ -113,6 +122,7 @@ _patch6 = { # Valid committer, but no reviewer.
"id": 106,
"bug_id": 77,
"url": "http://example.com/103",
+ "name": "ROLLOUT of r3489",
"is_obsolete": False,
"is_patch": True,
"commit-queue": "+",
@@ -125,6 +135,7 @@ _patch7 = { # Valid review, patch is marked obsolete.
"id": 107,
"bug_id": 76,
"url": "http://example.com/103",
+ "name": "Patch7",
"is_obsolete": True,
"is_patch": True,
"review": "+",
@@ -133,9 +144,8 @@ _patch7 = { # Valid review, patch is marked obsolete.
}
-# This must be defined before we define the bugs, thus we don't use
-# MockBugzilla.unassigned_email directly.
-_unassigned_email = "unassigned@example.com"
+# This matches one of Bug.unassigned_emails
+_unassigned_email = "webkit-unassigned@lists.webkit.org"
# FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
@@ -174,6 +184,15 @@ _bug4 = {
}
+class MockBuilder(object):
+
+ def name(self):
+ return "Mock builder name (Tests)"
+
+
+mock_builder = MockBuilder()
+
+
class MockBugzillaQueries(Mock):
def __init__(self, bugzilla):
@@ -221,8 +240,6 @@ class MockBugzilla(Mock):
bug_server_url = "http://example.com"
- unassigned_email = _unassigned_email
-
bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4)
attachment_cache = _id_to_object_dictionary(_patch1,
@@ -239,6 +256,23 @@ class MockBugzilla(Mock):
self.committers = CommitterList(reviewers=[Reviewer("Foo Bar",
"foo@bar.com")])
+ def create_bug(self,
+ bug_title,
+ bug_description,
+ component=None,
+ patch_file_object=None,
+ patch_description=None,
+ cc=None,
+ blocked=None,
+ mark_for_review=False,
+ mark_for_commit_queue=False):
+ log("MOCK create_bug")
+ log("bug_title: %s" % bug_title)
+ log("bug_description: %s" % bug_description)
+
+ def quips(self):
+ return ["Good artists copy. Great artists steal. - Pablo Picasso"]
+
def fetch_bug(self, bug_id):
return Bug(self.bug_cache.get(bug_id), self)
@@ -262,27 +296,93 @@ class MockBugzilla(Mock):
action_param = "&action=%s" % action
return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param)
+ def post_comment_to_bug(self, bug_id, comment_text, cc=None):
+ log("MOCK bug comment: bug_id=%s, cc=%s\n--- Begin comment ---\%s\n--- End comment ---\n" % (
+ bug_id, cc, comment_text))
-class MockBuildBot(Mock):
+ def add_patch_to_bug(self,
+ bug_id,
+ patch_file_object,
+ description,
+ comment_text=None,
+ mark_for_review=False,
+ mark_for_commit_queue=False,
+ mark_for_landing=False):
+ log("MOCK add_patch_to_bug: bug_id=%s, description=%s, mark_for_review=%s, mark_for_commit_queue=%s, mark_for_landing=%s" %
+ (bug_id, description, mark_for_review, mark_for_commit_queue, mark_for_landing))
+ log("-- Begin comment --")
+ log(comment_text)
+ log("-- End comment --")
- def builder_statuses(self):
- return [{
+
+class MockBuilder(object):
+ def __init__(self, name):
+ self._name = name
+
+ def force_build(self, username, comments):
+ log("MOCK: force_build: name=%s, username=%s, comments=%s" % (
+ self._name, username, comments))
+
+
+class MockBuildBot(object):
+ def __init__(self):
+ self._mock_builder1_status = {
"name": "Builder1",
"is_green": True,
- }, {
+ "activity": "building",
+ }
+ self._mock_builder2_status = {
"name": "Builder2",
"is_green": True,
- }]
+ "activity": "idle",
+ }
+
+ def builder_with_name(self, name):
+ return MockBuilder(name)
+
+ def builder_statuses(self):
+ return [
+ self._mock_builder1_status,
+ self._mock_builder2_status,
+ ]
def red_core_builders_names(self):
+ if not self._mock_builder2_status["is_green"]:
+ return [self._mock_builder2_status["name"]]
+ return []
+
+ def red_core_builders(self):
+ if not self._mock_builder2_status["is_green"]:
+ return [self._mock_builder2_status]
+ return []
+
+ def idle_red_core_builders(self):
+ if not self._mock_builder2_status["is_green"]:
+ return [self._mock_builder2_status]
return []
+ def last_green_revision(self):
+ return 9479
+
+ def light_tree_on_fire(self):
+ self._mock_builder2_status["is_green"] = False
+
+ def revisions_causing_failures(self):
+ return {
+ "29837": [mock_builder]
+ }
+
class MockSCM(Mock):
+ fake_checkout_root = os.path.realpath("/tmp") # realpath is needed to allow for Mac OS X's /private/tmp
+
def __init__(self):
Mock.__init__(self)
- self.checkout_root = os.getcwd()
+ # FIXME: We should probably use real checkout-root detection logic here.
+ # os.getcwd() can't work here because other parts of the code assume that "checkout_root"
+ # will actually be the root. Since getcwd() is wrong, use a globally fake root for now.
+ self.checkout_root = self.fake_checkout_root
def create_patch(self):
return "Patch1"
@@ -313,11 +413,40 @@ class MockSCM(Mock):
def svn_revision_from_commit_text(self, commit_text):
return "49824"
+
+class MockCheckout(object):
+
+ _committer_list = CommitterList()
+
+ def commit_info_for_revision(self, svn_revision):
+ return CommitInfo(svn_revision, "eric@webkit.org", {
+ "bug_id": 42,
+ "author_name": "Adam Barth",
+ "author_email": "abarth@webkit.org",
+ "author": self._committer_list.committer_by_email("abarth@webkit.org"),
+ "reviewer_text": "Darin Adler",
+ "reviewer": self._committer_list.committer_by_name("Darin Adler"),
+ })
+
+ def bug_id_for_revision(self, svn_revision):
+ return 12345
+
def modified_changelogs(self):
# Ideally we'd return something more interesting here. The problem is
- # that LandDiff will try to actually read the path from disk!
+ # that LandDiff will try to actually read the patch from disk!
return []
+ def commit_message_for_this_commit(self):
+ commit_message = Mock()
+ commit_message.message = lambda:"This is a fake commit message that is at least 50 characters."
+ return commit_message
+
+ def apply_patch(self, patch, force=False):
+ pass
+
+ def apply_reverse_diff(self, revision):
+ pass
+
class MockUser(object):
@@ -335,8 +464,19 @@ class MockUser(object):
return True
def open_url(self, url):
+ if url.startswith("file://"):
+ log("MOCK: user.open_url: file://...")
+ return
log("MOCK: user.open_url: %s" % url)
- pass
+
+
+class MockIRC(object):
+
+ def post(self, message):
+ log("MOCK: irc.post: %s" % message)
+
+ def disconnect(self):
+ log("MOCK: irc.disconnect")
class MockStatusServer(object):
@@ -347,22 +487,59 @@ class MockStatusServer(object):
def patch_status(self, queue_name, patch_id):
return None
+ def svn_revision(self, svn_revision):
+ return None
+
def update_status(self, queue_name, status, patch=None, results_file=None):
return 187
+ def update_svn_revision(self, svn_revision, broken_bot):
+ return 191
-class MockBugzillaTool():
- def __init__(self):
+class MockExecute(Mock):
+ def run_and_throw_if_fail(self, args, quiet=False):
+ return "MOCK output of child process"
+
+ def run_command(self,
+ args,
+ cwd=None,
+ input=None,
+ error_handler=None,
+ return_exit_code=False,
+ return_stderr=True):
+ return "MOCK output of child process"
+
+
+class MockTool():
+
+ def __init__(self, log_executive=False):
+ self.wakeup_event = threading.Event()
self.bugs = MockBugzilla()
self.buildbot = MockBuildBot()
- self.executive = Mock()
+ self.executive = MockExecute()
+ if log_executive:
+ self.executive.run_and_throw_if_fail = lambda args: log("MOCK run_and_throw_if_fail: %s" % args)
+ self._irc = None
self.user = MockUser()
self._scm = MockSCM()
+ self._checkout = MockCheckout()
self.status_server = MockStatusServer()
+ self.irc_password = "MOCK irc password"
+ self.codereview = Rietveld(self.executive)
def scm(self):
return self._scm
+ def checkout(self):
+ return self._checkout
+
+ def ensure_irc_connected(self, delegate):
+ if not self._irc:
+ self._irc = MockIRC()
+
+ def irc(self):
+ return self._irc
+
def path(self):
return "echo"
diff --git a/WebKitTools/Scripts/webkitpy/multicommandtool.py b/WebKitTools/Scripts/webkitpy/tool/multicommandtool.py
index 10cf426..7940c06 100644
--- a/WebKitTools/Scripts/webkitpy/multicommandtool.py
+++ b/WebKitTools/Scripts/webkitpy/tool/multicommandtool.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Google Inc. All rights reserved.
# Copyright (c) 2009 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -35,8 +35,8 @@ import sys
from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option
-from webkitpy.grammar import pluralize
-from webkitpy.webkit_logging import log
+from webkitpy.tool.grammar import pluralize
+from webkitpy.common.system.deprecated_logging import log
class Command(object):
@@ -263,6 +263,9 @@ class MultiCommandTool(object):
def path(self):
raise NotImplementedError, "subclasses must implement"
+ def command_completed(self):
+ pass
+
def should_show_in_main_help(self, command):
return command.show_in_main_help
@@ -296,4 +299,6 @@ class MultiCommandTool(object):
log(failure_reason)
return 0 # FIXME: Should this really be 0?
- return command.check_arguments_and_execute(options, args, self)
+ result = command.check_arguments_and_execute(options, args, self)
+ self.command_completed()
+ return result
diff --git a/WebKitTools/Scripts/webkitpy/multicommandtool_unittest.py b/WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py
index ae77e73..268ebf0 100644
--- a/WebKitTools/Scripts/webkitpy/multicommandtool_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 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
@@ -28,11 +28,13 @@
import sys
import unittest
-from multicommandtool import MultiCommandTool, Command
-from webkitpy.outputcapture import OutputCapture
from optparse import make_option
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.multicommandtool import MultiCommandTool, Command
+
+
class TrivialCommand(Command):
name = "trivial"
show_in_main_help = True
@@ -72,7 +74,7 @@ class TrivialTool(MultiCommandTool):
def __init__(self, commands=None):
MultiCommandTool.__init__(self, name="trivial-tool", commands=commands)
- def path():
+ def path(self):
return __file__
def should_execute_command(self, command):
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py b/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
new file mode 100644
index 0000000..d59cdc5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2010 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.
+
+# FIXME: Is this the right way to do this?
+from webkitpy.tool.steps.applypatch import ApplyPatch
+from webkitpy.tool.steps.applypatchwithlocalcommit import ApplyPatchWithLocalCommit
+from webkitpy.tool.steps.build import Build
+from webkitpy.tool.steps.checkstyle import CheckStyle
+from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
+from webkitpy.tool.steps.cleanworkingdirectorywithlocalcommits import CleanWorkingDirectoryWithLocalCommits
+from webkitpy.tool.steps.closebug import CloseBug
+from webkitpy.tool.steps.closebugforlanddiff import CloseBugForLandDiff
+from webkitpy.tool.steps.closepatch import ClosePatch
+from webkitpy.tool.steps.commit import Commit
+from webkitpy.tool.steps.confirmdiff import ConfirmDiff
+from webkitpy.tool.steps.createbug import CreateBug
+from webkitpy.tool.steps.editchangelog import EditChangeLog
+from webkitpy.tool.steps.ensurebuildersaregreen import EnsureBuildersAreGreen
+from webkitpy.tool.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded
+from webkitpy.tool.steps.obsoletepatches import ObsoletePatches
+from webkitpy.tool.steps.options import Options
+from webkitpy.tool.steps.postcodereview import PostCodeReview
+from webkitpy.tool.steps.postdiff import PostDiff
+from webkitpy.tool.steps.postdiffforcommit import PostDiffForCommit
+from webkitpy.tool.steps.postdiffforrevert import PostDiffForRevert
+from webkitpy.tool.steps.preparechangelogforrevert import PrepareChangeLogForRevert
+from webkitpy.tool.steps.preparechangelog import PrepareChangeLog
+from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle
+from webkitpy.tool.steps.reopenbugafterrollout import ReopenBugAfterRollout
+from webkitpy.tool.steps.revertrevision import RevertRevision
+from webkitpy.tool.steps.runtests import RunTests
+from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
+from webkitpy.tool.steps.update import Update
+from webkitpy.tool.steps.validatereviewer import ValidateReviewer
diff --git a/WebKitTools/Scripts/webkitpy/steps/abstractstep.py b/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py
index 639cf55..1ad343d 100644
--- a/WebKitTools/Scripts/webkitpy/steps/abstractstep.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py
@@ -26,8 +26,8 @@
# (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 webkitpy.webkit_logging import log
-from webkitpy.webkitport import WebKitPort
+from webkitpy.common.system.deprecated_logging import log
+from webkitpy.common.config.ports import WebKitPort
class AbstractStep(object):
@@ -50,7 +50,7 @@ class AbstractStep(object):
_well_known_keys = {
"diff" : lambda self: self._tool.scm().create_patch(),
- "changelogs" : lambda self: self._tool.scm().modified_changelogs(),
+ "changelogs" : lambda self: self._tool.checkout().modified_changelogs(),
}
def cached_lookup(self, state, key, promise=None):
diff --git a/WebKitTools/Scripts/webkitpy/steps/applypatch.py b/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
index aba81ae..66d0a03 100644
--- a/WebKitTools/Scripts/webkitpy/steps/applypatch.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class ApplyPatch(AbstractStep):
@classmethod
@@ -39,4 +39,4 @@ class ApplyPatch(AbstractStep):
def run(self, state):
log("Processing patch %s from bug %s." % (state["patch"].id(), state["patch"].bug_id()))
- self._tool.scm().apply_patch(state["patch"], force=self._options.non_interactive)
+ self._tool.checkout().apply_patch(state["patch"], force=self._options.non_interactive)
diff --git a/WebKitTools/Scripts/webkitpy/steps/applypatchwithlocalcommit.py b/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py
index bfaf52a..70ddfe5 100644
--- a/WebKitTools/Scripts/webkitpy/steps/applypatchwithlocalcommit.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py
@@ -26,8 +26,8 @@
# (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 webkitpy.steps.applypatch import ApplyPatch
-from webkitpy.steps.options import Options
+from webkitpy.tool.steps.applypatch import ApplyPatch
+from webkitpy.tool.steps.options import Options
class ApplyPatchWithLocalCommit(ApplyPatch):
@classmethod
@@ -39,5 +39,5 @@ class ApplyPatchWithLocalCommit(ApplyPatch):
def run(self, state):
ApplyPatch.run(self, state)
if self._options.local_commit:
- commit_message = self._tool.scm().commit_message_for_this_commit()
+ commit_message = self._tool.checkout().commit_message_for_this_commit()
self._tool.scm().commit_locally_with_message(commit_message.message() or state["patch"].name())
diff --git a/WebKitTools/Scripts/webkitpy/steps/build.py b/WebKitTools/Scripts/webkitpy/tool/steps/build.py
index 1823cff..f0570f9 100644
--- a/WebKitTools/Scripts/webkitpy/steps/build.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/build.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class Build(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/checkstyle.py b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py
index c8e20f8..63f0114 100644
--- a/WebKitTools/Scripts/webkitpy/steps/checkstyle.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py
@@ -28,10 +28,10 @@
import os
-from webkitpy.executive import ScriptError
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import error
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import error
class CheckStyle(AbstractStep):
@classmethod
diff --git a/WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectory.py b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
index 88e38f5..3768297 100644
--- a/WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectory.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
@@ -28,8 +28,8 @@
import os
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
class CleanWorkingDirectory(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectorywithlocalcommits.py b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py
index cabeba2..f06f94e 100644
--- a/WebKitTools/Scripts/webkitpy/steps/cleanworkingdirectorywithlocalcommits.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py
@@ -26,7 +26,7 @@
# (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 webkitpy.steps.cleanworkingdirectory import CleanWorkingDirectory
+from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
class CleanWorkingDirectoryWithLocalCommits(CleanWorkingDirectory):
def __init__(self, tool, options):
diff --git a/WebKitTools/Scripts/webkitpy/steps/closebug.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebug.py
index 2640ee3..d5059ea 100644
--- a/WebKitTools/Scripts/webkitpy/steps/closebug.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/closebug.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class CloseBug(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py
index 43a0c66..476d3af 100644
--- a/WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py
@@ -26,10 +26,10 @@
# (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 webkitpy.comments import bug_comment_from_commit_text
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.comments import bug_comment_from_commit_text
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class CloseBugForLandDiff(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py
index 73561ab..4e7f9e6 100644
--- a/WebKitTools/Scripts/webkitpy/steps/closebugforlanddiff_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py
@@ -28,14 +28,14 @@
import unittest
-from webkitpy.steps.closebugforlanddiff import CloseBugForLandDiff
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.mocktool import MockTool
+from webkitpy.tool.steps.closebugforlanddiff import CloseBugForLandDiff
class CloseBugForLandDiffTest(unittest.TestCase):
def test_empty_state(self):
capture = OutputCapture()
- step = CloseBugForLandDiff(MockBugzillaTool(), Mock())
+ step = CloseBugForLandDiff(MockTool(), Mock())
expected_stderr = "Committed r49824: <http://trac.webkit.org/changeset/49824>\nNo bug id provided.\n"
capture.assert_outputs(self, step.run, [{"commit_text" : "Mock commit text"}], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/steps/closepatch.py b/WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py
index f20fe7e..ff94df8 100644
--- a/WebKitTools/Scripts/webkitpy/steps/closepatch.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py
@@ -26,8 +26,8 @@
# (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 webkitpy.comments import bug_comment_from_commit_text
-from webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.comments import bug_comment_from_commit_text
+from webkitpy.tool.steps.abstractstep import AbstractStep
class ClosePatch(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/commit.py b/WebKitTools/Scripts/webkitpy/tool/steps/commit.py
new file mode 100644
index 0000000..294b41e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/commit.py
@@ -0,0 +1,37 @@
+# Copyright (C) 2010 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 webkitpy.tool.steps.abstractstep import AbstractStep
+
+
+class Commit(AbstractStep):
+ def run(self, state):
+ commit_message = self._tool.checkout().commit_message_for_this_commit()
+ if len(commit_message.message()) < 50:
+ raise Exception("Attempted to commit with a commit message shorter than 50 characters. Either your patch is missing a ChangeLog or webkit-patch may have a bug.")
+ state["commit_text"] = self._tool.scm().commit_with_message(commit_message.message())
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py
new file mode 100644
index 0000000..d08e477
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py
@@ -0,0 +1,74 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import urllib
+
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.prettypatch import PrettyPatch
+from webkitpy.common.system import logutils
+from webkitpy.common.system.executive import ScriptError
+
+
+_log = logutils.get_logger(__file__)
+
+
+class ConfirmDiff(AbstractStep):
+ @classmethod
+ def options(cls):
+ return [
+ Options.confirm,
+ ]
+
+ def _show_pretty_diff(self, diff):
+ try:
+ pretty_patch = PrettyPatch(self._tool.executive,
+ self._tool.scm().checkout_root)
+ pretty_diff_file = pretty_patch.pretty_diff_file(diff)
+ url = "file://%s" % urllib.quote(pretty_diff_file.name)
+ self._tool.user.open_url(url)
+ # We return the pretty_diff_file here because we need to keep the
+ # file alive until the user has had a chance to confirm the diff.
+ return pretty_diff_file
+ except ScriptError, e:
+ _log.warning("PrettyPatch failed. :(")
+ except OSError, e:
+ _log.warning("PrettyPatch unavailable.")
+
+ def run(self, state):
+ if not self._options.confirm:
+ return
+ diff = self.cached_lookup(state, "diff")
+ pretty_diff_file = self._show_pretty_diff(diff)
+ if not pretty_diff_file:
+ self._tool.user.page(diff)
+ diff_correct = self._tool.user.confirm("Was that diff correct?")
+ if pretty_diff_file:
+ pretty_diff_file.close()
+ if not diff_correct:
+ exit(1)
diff --git a/WebKitTools/Scripts/webkitpy/steps/createbug.py b/WebKitTools/Scripts/webkitpy/tool/steps/createbug.py
index 75bf17f..2f3d42c 100644
--- a/WebKitTools/Scripts/webkitpy/steps/createbug.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/createbug.py
@@ -26,8 +26,8 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
class CreateBug(AbstractStep):
@@ -42,4 +42,7 @@ class CreateBug(AbstractStep):
# No need to create a bug if we already have one.
if state.get("bug_id"):
return
- state["bug_id"] = self._tool.bugs.create_bug(state["bug_title"], state["bug_description"], component=self._options.component, cc=self._options.cc)
+ cc = self._options.cc
+ if not cc:
+ cc = state.get("bug_cc")
+ state["bug_id"] = self._tool.bugs.create_bug(state["bug_title"], state["bug_description"], blocked=state.get("bug_blocked"), component=self._options.component, cc=cc)
diff --git a/WebKitTools/Scripts/webkitpy/steps/editchangelog.py b/WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py
index d545c72..69c8732 100644
--- a/WebKitTools/Scripts/webkitpy/steps/editchangelog.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py
@@ -28,7 +28,7 @@
import os
-from webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.abstractstep import AbstractStep
class EditChangeLog(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/ensurebuildersaregreen.py b/WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py
index 96f265a..fd44564 100644
--- a/WebKitTools/Scripts/webkitpy/steps/ensurebuildersaregreen.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import error
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import error
class EnsureBuildersAreGreen(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/ensurelocalcommitifneeded.py b/WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py
index cecf891..4f799f2 100644
--- a/WebKitTools/Scripts/webkitpy/steps/ensurelocalcommitifneeded.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import error
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import error
class EnsureLocalCommitIfNeeded(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/metastep.py b/WebKitTools/Scripts/webkitpy/tool/steps/metastep.py
index 9f368de..7cbd1c5 100644
--- a/WebKitTools/Scripts/webkitpy/steps/metastep.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/metastep.py
@@ -26,7 +26,7 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.abstractstep import AbstractStep
# FIXME: Unify with StepSequence? I'm not sure yet which is the better design.
diff --git a/WebKitTools/Scripts/webkitpy/steps/obsoletepatches.py b/WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py
index dbdbabd..9f65d41 100644
--- a/WebKitTools/Scripts/webkitpy/steps/obsoletepatches.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py
@@ -26,10 +26,10 @@
# (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 webkitpy.grammar import pluralize
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class ObsoletePatches(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/options.py b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
index 8b28f27..7f76f55 100644
--- a/WebKitTools/Scripts/webkitpy/steps/options.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
@@ -29,18 +29,18 @@
from optparse import make_option
class Options(object):
- build = make_option("--no-build", action="store_false", dest="build", default=True, help="Commit without building first, implies --no-test.")
+ build = make_option("--build", action="store_true", dest="build", default=False, help="Build and run run-webkit-tests before committing.")
build_style = make_option("--build-style", action="store", dest="build_style", default=None, help="Whether to build debug, release, or both.")
cc = make_option("--cc", action="store", type="string", dest="cc", help="Comma-separated list of email addresses to carbon-copy.")
check_builders = make_option("--ignore-builders", action="store_false", dest="check_builders", default=True, help="Don't check to see if the build.webkit.org builders are green before landing.")
check_style = make_option("--ignore-style", action="store_false", dest="check_style", default=True, help="Don't check to see if the patch has proper style before uploading.")
clean = make_option("--no-clean", action="store_false", dest="clean", default=True, help="Don't check if the working directory is clean before applying patches")
close_bug = make_option("--no-close", action="store_false", dest="close_bug", default=True, help="Leave bug open after landing.")
- complete_rollout = make_option("--complete-rollout", action="store_true", dest="complete_rollout", help="Commit the revert and re-open the original bug.")
component = make_option("--component", action="store", type="string", dest="component", help="Component for the new bug.")
confirm = make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Skip confirmation steps.")
description = make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")")
email = make_option("--email", action="store", type="string", dest="email", help="Email address to use in ChangeLogs.")
+ fancy_review = make_option("--fancy-review", action="store_true", dest="fancy_review", default=False, help="(Experimental) Upload the patch to Rietveld code review tool.")
force_clean = make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)")
local_commit = make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch")
non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.")
@@ -52,5 +52,5 @@ class Options(object):
request_commit = make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review.")
review = make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review.")
reviewer = make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER.")
- test = make_option("--no-test", action="store_false", dest="test", default=True, help="Commit without running run-webkit-tests.")
+ test = make_option("--test", action="store_true", dest="test", default=False, help="Commit without running run-webkit-tests")
update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py b/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py
new file mode 100644
index 0000000..3e7ed76
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py
@@ -0,0 +1,74 @@
+# Copyright (C) 2010 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 webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+
+
+class PostCodeReview(AbstractStep):
+ @classmethod
+ def options(cls):
+ return [
+ Options.cc,
+ Options.description,
+ Options.fancy_review,
+ Options.review,
+ ]
+
+ def run(self, state):
+ if not self._options.fancy_review:
+ return
+ # FIXME: This will always be None because we don't retrieve the issue
+ # number from the ChangeLog yet.
+ codereview_issue = state.get("codereview_issue")
+ message = self._options.description
+ if not message:
+ # If we have an issue number, then the message becomes the label
+ # of the new patch. Otherwise, it becomes the title of the whole
+ # issue.
+ if codereview_issue:
+ message = "Updated patch"
+ elif state.get("bug_title"):
+ # This is the common case for the the first "upload" command.
+ message = state.get("bug_title")
+ elif state.get("bug_id"):
+ # This is the common case for the "post" command and
+ # subsequent runs of the "upload" command. In this case,
+ # I'd rather add the new patch to the existing issue, but
+ # that's not implemented yet.
+ message = "Code review for %s" % self._tool.bugs.bug_url_for_bug_id(state["bug_id"])
+ else:
+ # Unreachable with our current commands, but we might hit
+ # this case if we support bug-less code reviews.
+ message = "Code review"
+ created_issue = self._tool.codereview.post(message=message,
+ codereview_issue=codereview_issue,
+ cc=self._options.cc)
+ if created_issue:
+ # FIXME: Record the issue number in the ChangeLog.
+ state["codereview_issue"] = created_issue
diff --git a/WebKitTools/Scripts/webkitpy/steps/postdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py
index a5ba2a4..6a3dee4 100644
--- a/WebKitTools/Scripts/webkitpy/steps/postdiff.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py
@@ -28,8 +28,8 @@
import StringIO
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
class PostDiff(AbstractStep):
@@ -46,6 +46,12 @@ class PostDiff(AbstractStep):
diff = self.cached_lookup(state, "diff")
diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object
description = self._options.description or "Patch"
- self._tool.bugs.add_patch_to_bug(state["bug_id"], diff_file, description, mark_for_review=self._options.review, mark_for_commit_queue=self._options.request_commit)
+ comment_text = None
+ codereview_issue = state.get("codereview_issue")
+ # Include codereview issue number in patch name. This is a bit of a hack,
+ # but it makes doing the rietveld integration a lot easier.
+ if codereview_issue:
+ description += "-%s" % state["codereview_issue"]
+ self._tool.bugs.add_patch_to_bug(state["bug_id"], diff_file, description, comment_text=comment_text, mark_for_review=self._options.review, mark_for_commit_queue=self._options.request_commit)
if self._options.open_bug:
self._tool.user.open_url(self._tool.bugs.bug_url_for_bug_id(state["bug_id"]))
diff --git a/WebKitTools/Scripts/webkitpy/steps/postdiffforcommit.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py
index 449381c..03b9e78 100644
--- a/WebKitTools/Scripts/webkitpy/steps/postdiffforcommit.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py
@@ -28,7 +28,7 @@
import StringIO
-from webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.abstractstep import AbstractStep
class PostDiffForCommit(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py
new file mode 100644
index 0000000..3b9da04
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import StringIO
+
+from webkitpy.common.net.bugzilla import Attachment
+from webkitpy.tool.steps.abstractstep import AbstractStep
+
+
+class PostDiffForRevert(AbstractStep):
+ def run(self, state):
+ comment_text = "Any committer can land this patch automatically by \
+marking it commit-queue+. The commit-queue will build and test \
+the patch before landing to ensure that the rollout will be \
+successful. This process takes approximately 15 minutes.\n\n\
+If you would like to land the rollout faster, you can use the \
+following command:\n\n\
+ webkit-patch land-attachment ATTACHMENT_ID --ignore-builders\n\n\
+where ATTACHMENT_ID is the ID of this attachment."
+ self._tool.bugs.add_patch_to_bug(
+ state["bug_id"],
+ StringIO.StringIO(self.cached_lookup(state, "diff")),
+ "%s%s" % (Attachment.rollout_preamble, state["revision"]),
+ comment_text=comment_text,
+ mark_for_review=False,
+ mark_for_commit_queue=True)
diff --git a/WebKitTools/Scripts/webkitpy/steps/preparechangelog.py b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py
index bd41f0b..fcb40be 100644
--- a/WebKitTools/Scripts/webkitpy/steps/preparechangelog.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py
@@ -28,10 +28,10 @@
import os
-from webkitpy.executive import ScriptError
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import error
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import error
class PrepareChangeLog(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/preparechangelogforrevert.py b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
index 88e5134..f7d9cd3 100644
--- a/WebKitTools/Scripts/webkitpy/steps/preparechangelogforrevert.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
@@ -28,20 +28,15 @@
import os
-from webkitpy.changelogs import ChangeLog
-from webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.common.checkout.changelog import ChangeLog
+from webkitpy.tool.steps.abstractstep import AbstractStep
class PrepareChangeLogForRevert(AbstractStep):
def run(self, state):
- # First, discard the ChangeLog changes from the rollout.
- os.chdir(self._tool.scm().checkout_root)
- changelog_paths = self._tool.scm().modified_changelogs()
- self._tool.scm().revert_files(changelog_paths)
-
- # Second, make new ChangeLog entries for this rollout.
# This could move to prepare-ChangeLog by adding a --revert= option.
self._run_script("prepare-ChangeLog")
+ changelog_paths = self._tool.checkout().modified_changelogs()
bug_url = self._tool.bugs.bug_url_for_bug_id(state["bug_id"]) if state["bug_id"] else None
for changelog_path in changelog_paths:
# FIXME: Seems we should prepare the message outside of changelogs.py and then just pass in
diff --git a/WebKitTools/Scripts/webkitpy/steps/promptforbugortitle.py b/WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py
index fb2f877..31c913c 100644
--- a/WebKitTools/Scripts/webkitpy/steps/promptforbugortitle.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py
@@ -26,7 +26,7 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.abstractstep import AbstractStep
class PromptForBugOrTitle(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/confirmdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py
index fc28f8f..f369ca9 100644
--- a/WebKitTools/Scripts/webkitpy/steps/confirmdiff.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py
@@ -26,22 +26,19 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import error
+from webkitpy.tool.comments import bug_comment_from_commit_text
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.common.system.deprecated_logging import log
-class ConfirmDiff(AbstractStep):
- @classmethod
- def options(cls):
- return [
- Options.confirm,
- ]
-
+class ReopenBugAfterRollout(AbstractStep):
def run(self, state):
- if not self._options.confirm:
+ commit_comment = bug_comment_from_commit_text(self._tool.scm(), state["commit_text"])
+ comment_text = "Reverted r%s for reason:\n\n%s\n\n%s" % (state["revision"], state["reason"], commit_comment)
+
+ bug_id = state["bug_id"]
+ if not bug_id:
+ log(comment_text)
+ log("No bugs were updated.")
return
- diff = self.cached_lookup(state, "diff")
- self._tool.user.page(diff)
- if not self._tool.user.confirm("Was that diff correct?"):
- exit(1)
+ self._tool.bugs.reopen_bug(bug_id, comment_text)
diff --git a/WebKitTools/Scripts/webkitpy/steps/revertrevision.py b/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
index ce6c263..81b6bcb 100644
--- a/WebKitTools/Scripts/webkitpy/steps/revertrevision.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.abstractstep import AbstractStep
class RevertRevision(AbstractStep):
def run(self, state):
- self._tool.scm().apply_reverse_diff(state["revision"])
+ self._tool.checkout().apply_reverse_diff(state["revision"])
diff --git a/WebKitTools/Scripts/webkitpy/steps/runtests.py b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
index ebe809f..55d8c62 100644
--- a/WebKitTools/Scripts/webkitpy/steps/runtests.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
@@ -26,15 +26,14 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class RunTests(AbstractStep):
@classmethod
def options(cls):
return [
- Options.build,
Options.test,
Options.non_interactive,
Options.quiet,
@@ -42,8 +41,6 @@ class RunTests(AbstractStep):
]
def run(self, state):
- if not self._options.build:
- return
if not self._options.test:
return
diff --git a/WebKitTools/Scripts/webkitpy/steps/steps_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
index 3e6a032..40bee90 100644
--- a/WebKitTools/Scripts/webkitpy/steps/steps_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
@@ -28,17 +28,17 @@
import unittest
-from webkitpy.steps.update import Update
-from webkitpy.steps.promptforbugortitle import PromptForBugOrTitle
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
-from webkitpy.mock import Mock
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.mocktool import MockTool
+from webkitpy.tool.steps.update import Update
+from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle
class StepsTest(unittest.TestCase):
def _run_step(self, step, tool=None, options=None, state=None):
if not tool:
- tool = MockBugzillaTool()
+ tool = MockTool()
if not options:
options = Mock()
if not state:
@@ -51,6 +51,6 @@ class StepsTest(unittest.TestCase):
self._run_step(Update, options)
def test_prompt_for_bug_or_title_step(self):
- tool = MockBugzillaTool()
+ tool = MockTool()
tool.user.prompt = lambda message: 42
self._run_step(PromptForBugOrTitle, tool=tool)
diff --git a/WebKitTools/Scripts/webkitpy/steps/update.py b/WebKitTools/Scripts/webkitpy/tool/steps/update.py
index 0f45671..c98eba7 100644
--- a/WebKitTools/Scripts/webkitpy/steps/update.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/update.py
@@ -26,9 +26,9 @@
# (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 webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log
class Update(AbstractStep):
diff --git a/WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreview_unittests.py b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py
index 102a454..0534718 100644
--- a/WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreview_unittests.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py
@@ -28,19 +28,19 @@
import unittest
-from webkitpy.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
-from webkitpy.mock import Mock
-from webkitpy.mock_bugzillatool import MockBugzillaTool
-from webkitpy.outputcapture import OutputCapture
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.mocktool import MockTool
+from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
class UpdateChangeLogsWithReviewerTest(unittest.TestCase):
def test_guess_reviewer_from_bug(self):
capture = OutputCapture()
- step = UpdateChangeLogsWithReviewer(MockBugzillaTool(), Mock())
+ step = UpdateChangeLogsWithReviewer(MockTool(), Mock())
expected_stderr = "0 reviewed patches on bug 75, cannot infer reviewer.\n"
capture.assert_outputs(self, step._guess_reviewer_from_bug, [75], expected_stderr=expected_stderr)
def test_empty_state(self):
capture = OutputCapture()
- step = UpdateChangeLogsWithReviewer(MockBugzillaTool(), Mock())
+ step = UpdateChangeLogsWithReviewer(MockTool(), Mock())
capture.assert_outputs(self, step.run, [{}])
diff --git a/WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreviewer.py b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
index 90fdc35..a35ed8c 100644
--- a/WebKitTools/Scripts/webkitpy/steps/updatechangelogswithreviewer.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
@@ -28,11 +28,11 @@
import os
-from webkitpy.changelogs import ChangeLog
-from webkitpy.grammar import pluralize
-from webkitpy.steps.abstractstep import AbstractStep
-from webkitpy.steps.options import Options
-from webkitpy.webkit_logging import log, error
+from webkitpy.common.checkout.changelog import ChangeLog
+from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.tool.steps.options import Options
+from webkitpy.common.system.deprecated_logging import log, error
class UpdateChangeLogsWithReviewer(AbstractStep):
@classmethod
@@ -67,5 +67,5 @@ class UpdateChangeLogsWithReviewer(AbstractStep):
return
os.chdir(self._tool.scm().checkout_root)
- for changelog_path in self._tool.scm().modified_changelogs():
+ for changelog_path in self._tool.checkout().modified_changelogs():
ChangeLog(changelog_path).set_reviewer(reviewer)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py
new file mode 100644
index 0000000..80b2c5d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py
@@ -0,0 +1,64 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import re
+
+from webkitpy.common.checkout.changelog import ChangeLog
+from webkitpy.tool.steps.abstractstep import AbstractStep
+from webkitpy.common.system.deprecated_logging import error, log
+
+
+# FIXME: Some of this logic should probably be unified with CommitterValidator?
+class ValidateReviewer(AbstractStep):
+ # FIXME: This should probably move onto ChangeLogEntry
+ def _has_valid_reviewer(self, changelog_entry):
+ if changelog_entry.reviewer():
+ return True
+ if re.search("unreviewed", changelog_entry.contents(), re.IGNORECASE):
+ return True
+ if re.search("rubber[ -]stamp", changelog_entry.contents(), re.IGNORECASE):
+ return True
+ return False
+
+ def run(self, state):
+ # FIXME: For now we disable this check when a user is driving the script
+ # this check is too draconian (and too poorly tested) to foist upon users.
+ if not self._options.non_interactive:
+ return
+ # FIXME: We should figure out how to handle the current working
+ # directory issue more globally.
+ os.chdir(self._tool.scm().checkout_root)
+ for changelog_path in self._tool.checkout().modified_changelogs():
+ changelog_entry = ChangeLog(changelog_path).latest_entry()
+ if self._has_valid_reviewer(changelog_entry):
+ continue
+ reviewer_text = changelog_entry.reviewer_text()
+ if reviewer_text:
+ log("%s found in %s does not appear to be a valid reviewer according to committers.py." % (reviewer_text, changelog_path))
+ error('%s neither lists a valid reviewer nor contains the string "Unreviewed" or "Rubber stamp" (case insensitive).' % changelog_path)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py
new file mode 100644
index 0000000..9105102
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py
@@ -0,0 +1,58 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.checkout.changelog import ChangeLogEntry
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.thirdparty.mock import Mock
+from webkitpy.tool.mocktool import MockTool
+from webkitpy.tool.steps.validatereviewer import ValidateReviewer
+
+class ValidateReviewerTest(unittest.TestCase):
+ _boilerplate_entry = '''2009-08-19 Eric Seidel <eric@webkit.org>
+
+ REVIEW_LINE
+
+ * Scripts/bugzilla-tool:
+'''
+
+ def _test_review_text(self, step, text, expected):
+ contents = self._boilerplate_entry.replace("REVIEW_LINE", text)
+ entry = ChangeLogEntry(contents)
+ self.assertEqual(step._has_valid_reviewer(entry), expected)
+
+ def test_has_valid_reviewer(self):
+ step = ValidateReviewer(MockTool(), Mock())
+ self._test_review_text(step, "Reviewed by Eric Seidel.", True)
+ self._test_review_text(step, "Reviewed by Eric Seidel", True) # Not picky about the '.'
+ self._test_review_text(step, "Reviewed by Eric.", False)
+ self._test_review_text(step, "Reviewed by Eric C Seidel.", False)
+ self._test_review_text(step, "Rubber-stamped by Eric.", True)
+ self._test_review_text(step, "Rubber stamped by Eric.", True)
+ self._test_review_text(step, "Unreviewed build fix.", True)
diff --git a/WebKitTools/TestResultServer/app.yaml b/WebKitTools/TestResultServer/app.yaml
new file mode 100644
index 0000000..e51af84
--- /dev/null
+++ b/WebKitTools/TestResultServer/app.yaml
@@ -0,0 +1,19 @@
+application: test-results
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /stylesheets
+ static_dir: stylesheets
+
+- url: /testfile/delete
+ script: main.py
+ login: admin
+
+- url: /dashboards/delete
+ script: main.py
+ login: admin
+
+- url: /.*
+ script: main.py
diff --git a/WebKitTools/TestResultServer/handlers/__init__.py b/WebKitTools/TestResultServer/handlers/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/TestResultServer/handlers/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/TestResultServer/handlers/dashboardhandler.py b/WebKitTools/TestResultServer/handlers/dashboardhandler.py
new file mode 100644
index 0000000..45bc471
--- /dev/null
+++ b/WebKitTools/TestResultServer/handlers/dashboardhandler.py
@@ -0,0 +1,120 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import mimetypes
+import urllib2
+
+from google.appengine.api import users
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+from model.dashboardfile import DashboardFile
+
+PARAM_FILE = "file"
+
+def get_content_type(filename):
+ return mimetypes.guess_type(filename)[0] or "application/octet-stream"
+
+
+class GetDashboardFile(webapp.RequestHandler):
+ def get(self, resource):
+ if not resource:
+ logging.debug("Getting dashboard file list.")
+ return self._get_file_list()
+
+ filename = str(urllib2.unquote(resource))
+
+ logging.debug("Getting dashboard file: %s", filename)
+
+ files = DashboardFile.get_files(filename)
+ if not files:
+ logging.error("Failed to find dashboard file: %s, request: %s",
+ filename, self.request)
+ self.response.set_status(404)
+ return
+
+ content_type = "%s; charset=utf-8" % get_content_type(filename)
+ logging.info("content type: %s", content_type)
+ self.response.headers["Content-Type"] = content_type
+ self.response.out.write(files[0].data)
+
+ def _get_file_list(self):
+ logging.info("getting dashboard file list.")
+
+ files = DashboardFile.get_files("", 100)
+ if not files:
+ logging.info("Failed to find dashboard files.")
+ self.response.set_status(404)
+ return
+
+ template_values = {
+ "admin": users.is_current_user_admin(),
+ "files": files,
+ }
+ self.response.out.write(
+ template.render("templates/dashboardfilelist.html",
+ template_values))
+
+
+class UpdateDashboardFile(webapp.RequestHandler):
+ def get(self):
+ files = self.request.get_all(PARAM_FILE)
+ if not files:
+ files = ["flakiness_dashboard.html",
+ "dashboard_base.js",
+ "aggregate_results.html"]
+
+ errors = []
+ for file in files:
+ if not DashboardFile.update_file(file):
+ errors.append("Failed to update file: %s" % file)
+
+ if errors:
+ messages = "; ".join(errors)
+ logging.warning(messages)
+ self.response.set_status(500, messages)
+ self.response.out.write("FAIL")
+ else:
+ self.response.set_status(200)
+ self.response.out.write("OK")
+
+
+class DeleteDashboardFile(webapp.RequestHandler):
+ def get(self):
+ files = self.request.get_all(PARAM_FILE)
+ if not files:
+ logging.warning("No dashboard file to delete.")
+ self.response.set_status(400)
+ return
+
+ for file in files:
+ DashboardFile.delete_file(file)
+
+ # Display dashboard file list after deleting the file.
+ self.redirect("/dashboards/")
diff --git a/WebKitTools/TestResultServer/handlers/menu.py b/WebKitTools/TestResultServer/handlers/menu.py
new file mode 100644
index 0000000..ad2599d
--- /dev/null
+++ b/WebKitTools/TestResultServer/handlers/menu.py
@@ -0,0 +1,64 @@
+# Copyright (C) 2010 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 google.appengine.api import users
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+menu = [
+ ["List of test files", "/testfile"],
+ ["List of results.json files", "/testfile?name=results.json"],
+ ["List of expectations.json files", "/testfile?name=expectations.json"],
+ ["Upload test file", "/testfile/uploadform"],
+ ["List of dashboard files", "/dashboards/"],
+ ["Update dashboard files", "/dashboards/update"],
+]
+
+
+class Menu(webapp.RequestHandler):
+ def get(self):
+ user = users.get_current_user()
+ if user:
+ user_email = user.email()
+ login_text = "Sign out"
+ login_url = users.create_logout_url(self.request.uri)
+ else:
+ user_email = ""
+ login_text = "Sign in"
+ login_url = users.create_login_url(self.request.uri)
+
+ template_values = {
+ "user_email": user_email,
+ "login_text": login_text,
+ "login_url": login_url,
+ "menu": menu,
+ }
+
+ self.response.out.write(
+ template.render("templates/menu.html", template_values))
+
diff --git a/WebKitTools/TestResultServer/handlers/testfilehandler.py b/WebKitTools/TestResultServer/handlers/testfilehandler.py
new file mode 100644
index 0000000..972b606
--- /dev/null
+++ b/WebKitTools/TestResultServer/handlers/testfilehandler.py
@@ -0,0 +1,221 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import urllib
+
+from google.appengine.api import users
+from google.appengine.ext import blobstore
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import blobstore_handlers
+from google.appengine.ext.webapp import template
+
+from model.testfile import TestFile
+
+PARAM_BUILDER = "builder"
+PARAM_DIR = "dir"
+PARAM_FILE = "file"
+PARAM_NAME = "name"
+PARAM_KEY = "key"
+PARAM_TEST_TYPE = "testtype"
+
+
+class DeleteFile(webapp.RequestHandler):
+ """Delete test file for a given builder and name from datastore (metadata) and blobstore (file data)."""
+
+ def get(self):
+ key = self.request.get(PARAM_KEY)
+ builder = self.request.get(PARAM_BUILDER)
+ test_type = self.request.get(PARAM_TEST_TYPE)
+ name = self.request.get(PARAM_NAME)
+
+ logging.debug(
+ "Deleting File, builder: %s, test_type: %s, name: %s, blob key: %s.",
+ builder, test_type, name, key)
+
+ TestFile.delete_file(key, builder, test_type, name, 100)
+
+ # Display file list after deleting the file.
+ self.redirect("/testfile?builder=%s&testtype=%s&name=%s"
+ % (builder, test_type, name))
+
+
+class GetFile(blobstore_handlers.BlobstoreDownloadHandler):
+ """Get file content or list of files for given builder and name."""
+
+ def _get_file_list(self, builder, test_type, name):
+ """Get and display a list of files that matches builder and file name.
+
+ Args:
+ builder: builder name
+ test_type: type of the test
+ name: file name
+ """
+
+ files = TestFile.get_files(builder, test_type, name, 100)
+ if not files:
+ logging.info("File not found, builder: %s, test_type: %s, name: %s.",
+ builder, test_type, name)
+ self.response.out.write("File not found")
+ return
+
+ template_values = {
+ "admin": users.is_current_user_admin(),
+ "builder": builder,
+ "test_type": test_type,
+ "name": name,
+ "files": files,
+ }
+ self.response.out.write(template.render("templates/showfilelist.html",
+ template_values))
+
+ def _get_file_content(self, builder, test_type, name):
+ """Return content of the file that matches builder and file name.
+
+ Args:
+ builder: builder name
+ test_type: type of the test
+ name: file name
+ """
+
+ files = TestFile.get_files(builder, test_type, name, 1)
+ if not files:
+ logging.info("File not found, builder: %s, test_type: %s, name: %s.",
+ builder, test_type, name)
+ return
+
+ blob_key = files[0].blob_key
+ blob_info = blobstore.get(blob_key)
+ if blob_info:
+ self.send_blob(blob_info, "text/plain")
+
+ def get(self):
+ builder = self.request.get(PARAM_BUILDER)
+ test_type = self.request.get(PARAM_TEST_TYPE)
+ name = self.request.get(PARAM_NAME)
+ dir = self.request.get(PARAM_DIR)
+
+ logging.debug(
+ "Getting files, builder: %s, test_type: %s, name: %s.",
+ builder, test_type, name)
+
+ # If parameter "dir" is specified or there is no builder or filename
+ # specified in the request, return list of files, otherwise, return
+ # file content.
+ if dir or not builder or not name:
+ return self._get_file_list(builder, test_type, name)
+ else:
+ return self._get_file_content(builder, test_type, name)
+
+
+class GetUploadUrl(webapp.RequestHandler):
+ """Get an url for uploading file to blobstore. A special url is required for each blobsotre upload."""
+
+ def get(self):
+ upload_url = blobstore.create_upload_url("/testfile/upload")
+ logging.info("Getting upload url: %s.", upload_url)
+ self.response.out.write(upload_url)
+
+
+class Upload(blobstore_handlers.BlobstoreUploadHandler):
+ """Upload file to blobstore."""
+
+ def post(self):
+ uploaded_files = self.get_uploads("file")
+ if not uploaded_files:
+ return self._upload_done([("Missing upload file field.")])
+
+ builder = self.request.get(PARAM_BUILDER)
+ if not builder:
+ for blob_info in uploaded_files:
+ blob_info.delete()
+
+ return self._upload_done([("Missing builder parameter in upload request.")])
+
+ test_type = self.request.get(PARAM_TEST_TYPE)
+
+ logging.debug(
+ "Processing upload request, builder: %s, test_type: %s.",
+ builder, test_type)
+
+ errors = []
+ for blob_info in uploaded_files:
+ tf = TestFile.update_file(builder, test_type, blob_info)
+ if not tf:
+ errors.append(
+ "Upload failed, builder: %s, test_type: %s, name: %s." %
+ (builder, test_type, blob_info.filename))
+ blob_info.delete()
+
+ return self._upload_done(errors)
+
+ def _upload_done(self, errors):
+ logging.info("upload done.")
+
+ error_messages = []
+ for error in errors:
+ logging.info(error)
+ error_messages.append("error=%s" % urllib.quote(error))
+
+ if error_messages:
+ redirect_url = "/uploadfail?%s" % "&".join(error_messages)
+ else:
+ redirect_url = "/uploadsuccess"
+
+ logging.info(redirect_url)
+ # BlobstoreUploadHandler requires redirect at the end.
+ self.redirect(redirect_url)
+
+
+class UploadForm(webapp.RequestHandler):
+ """Show a form so user can submit a file to blobstore."""
+
+ def get(self):
+ upload_url = blobstore.create_upload_url("/testfile/upload")
+ template_values = {
+ "upload_url": upload_url,
+ }
+ self.response.out.write(template.render("templates/uploadform.html",
+ template_values))
+
+class UploadStatus(webapp.RequestHandler):
+ """Return status of file uploading"""
+
+ def get(self):
+ logging.debug("Update status")
+
+ if self.request.path == "/uploadsuccess":
+ self.response.set_status(200)
+ self.response.out.write("OK")
+ else:
+ errors = self.request.params.getall("error")
+ if errors:
+ messages = "FAIL: " + "; ".join(errors)
+ logging.warning(messages)
+ self.response.set_status(500, messages)
+ self.response.out.write("FAIL")
diff --git a/WebKitTools/TestResultServer/index.yaml b/WebKitTools/TestResultServer/index.yaml
new file mode 100644
index 0000000..50284dc
--- /dev/null
+++ b/WebKitTools/TestResultServer/index.yaml
@@ -0,0 +1,50 @@
+indexes:
+
+# AUTOGENERATED
+
+# This index.yaml is automatically updated whenever the dev_appserver
+# detects that a new type of query is run. If you want to manage the
+# index.yaml file manually, remove the above marker line (the line
+# saying "# AUTOGENERATED"). If you want to manage some indexes
+# manually, move them above the marker line. The index.yaml file is
+# automatically uploaded to the admin console when you next deploy
+# your application using appcfg.py.
+
+- kind: DashboardFile
+ properties:
+ - name: name
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: builder
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: builder
+ - name: name
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: builder
+ - name: name
+ - name: test_type
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: name
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: test_type
+ - name: date
+ direction: desc
diff --git a/WebKitTools/TestResultServer/main.py b/WebKitTools/TestResultServer/main.py
new file mode 100644
index 0000000..7a0d237
--- /dev/null
+++ b/WebKitTools/TestResultServer/main.py
@@ -0,0 +1,60 @@
+# Copyright (C) 2010 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.
+
+# Request a modern Django
+from google.appengine.dist import use_library
+use_library('django', '1.1')
+
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp.util import run_wsgi_app
+
+from handlers import dashboardhandler
+from handlers import menu
+from handlers import testfilehandler
+
+routes = [
+ ('/dashboards/delete', dashboardhandler.DeleteDashboardFile),
+ ('/dashboards/update', dashboardhandler.UpdateDashboardFile),
+ ('/dashboards/([^?]+)?', dashboardhandler.GetDashboardFile),
+ ('/testfile/delete', testfilehandler.DeleteFile),
+ ('/testfile/uploadurl', testfilehandler.GetUploadUrl),
+ ('/testfile/upload', testfilehandler.Upload),
+ ('/testfile/uploadform', testfilehandler.UploadForm),
+ ('/testfile/?', testfilehandler.GetFile),
+ ('/uploadfail', testfilehandler.UploadStatus),
+ ('/uploadsuccess', testfilehandler.UploadStatus),
+ ('/*|/menu', menu.Menu),
+]
+
+application = webapp.WSGIApplication(routes, debug=True)
+
+def main():
+ run_wsgi_app(application)
+
+if __name__ == "__main__":
+ main()
diff --git a/WebKitTools/TestResultServer/model/__init__.py b/WebKitTools/TestResultServer/model/__init__.py
new file mode 100644
index 0000000..ef65bee
--- /dev/null
+++ b/WebKitTools/TestResultServer/model/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/WebKitTools/TestResultServer/model/dashboardfile.py b/WebKitTools/TestResultServer/model/dashboardfile.py
new file mode 100644
index 0000000..c74f071
--- /dev/null
+++ b/WebKitTools/TestResultServer/model/dashboardfile.py
@@ -0,0 +1,116 @@
+# Copyright (C) 2010 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 datetime import datetime
+import logging
+import urllib
+import urllib2
+
+from google.appengine.ext import db
+
+SVN_PATH_DASHBOARD = ("http://src.chromium.org/viewvc/chrome/trunk/tools/"
+ "dashboards/")
+
+class DashboardFile(db.Model):
+ name = db.StringProperty()
+ data = db.BlobProperty()
+ date = db.DateTimeProperty(auto_now_add=True)
+
+ @classmethod
+ def get_files(cls, name, limit=1):
+ query = DashboardFile.all()
+ if name:
+ query = query.filter("name =", name)
+ return query.order("-date").fetch(limit)
+
+ @classmethod
+ def add_file(cls, name, data):
+ file = DashboardFile()
+ file.name = name
+ file.data = db.Blob(data)
+ file.put()
+
+ logging.debug("Dashboard file saved, name: %s.", name)
+
+ return file
+
+ @classmethod
+ def grab_file_from_svn(cls, name):
+ logging.debug("Grab file from SVN, name: %s.", name)
+
+ url = SVN_PATH_DASHBOARD + urllib.quote_plus(name)
+
+ logging.info("Grab file from SVN, url: %s.", url)
+ try:
+ file = urllib2.urlopen(url)
+ if not file:
+ logging.error("Failed to grab dashboard file: %s.", url)
+ return None
+
+ return file.read()
+ except urllib2.HTTPError, e:
+ logging.error("Failed to grab dashboard file: %s", str(e))
+ except urllib2.URLError, e:
+ logging.error("Failed to grab dashboard file: %s", str(e))
+
+ return None
+
+ @classmethod
+ def update_file(cls, name):
+ data = cls.grab_file_from_svn(name)
+ if not data:
+ return None
+
+ logging.info("Got file from SVN.")
+
+ files = cls.get_files(name)
+ if not files:
+ logging.info("No existing file, added as new file.")
+ return cls.add_file(name, data)
+
+ logging.debug("Updating existing file.")
+ file = files[0]
+ file.data = data
+ file.date = datetime.now()
+ file.put()
+
+ logging.info("Dashboard file replaced, name: %s.", name)
+
+ return file
+
+ @classmethod
+ def delete_file(cls, name):
+ files = cls.get_files(name)
+ if not files:
+ logging.warning("File not found, name: %s.", name)
+ return False
+
+ for file in files:
+ file.delete()
+
+ return True
diff --git a/WebKitTools/TestResultServer/model/testfile.py b/WebKitTools/TestResultServer/model/testfile.py
new file mode 100644
index 0000000..35ab967
--- /dev/null
+++ b/WebKitTools/TestResultServer/model/testfile.py
@@ -0,0 +1,122 @@
+# Copyright (C) 2010 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 datetime import datetime
+import logging
+
+from google.appengine.ext import blobstore
+from google.appengine.ext import db
+
+
+class TestFile(db.Model):
+ builder = db.StringProperty()
+ name = db.StringProperty()
+ test_type = db.StringProperty()
+ blob_key = db.StringProperty()
+ date = db.DateTimeProperty(auto_now_add=True)
+
+ @classmethod
+ def delete_file(cls, key, builder, test_type, name, limit):
+ if key:
+ file = db.get(key)
+ if not file:
+ logging.warning("File not found, key: %s.", key)
+ return False
+
+ file._delete_all()
+ else:
+ files = cls.get_files(builder, test_type, name, limit)
+ if not files:
+ logging.warning(
+ "File not found, builder: %s, test_type:%s, name: %s.",
+ builder, test_type, name)
+ return False
+
+ for file in files:
+ file._delete_all()
+
+ return True
+
+ @classmethod
+ def get_files(cls, builder, test_type, name, limit):
+ query = TestFile.all()
+ if builder:
+ query = query.filter("builder =", builder)
+ if test_type:
+ query = query.filter("test_type =", test_type)
+ if name:
+ query = query.filter("name =", name)
+
+ return query.order("-date").fetch(limit)
+
+ @classmethod
+ def add_file(cls, builder, test_type, blob_info):
+ file = TestFile()
+ file.builder = builder
+ file.test_type = test_type
+ file.name = blob_info.filename
+ file.blob_key = str(blob_info.key())
+ file.put()
+
+ logging.info(
+ "File saved, builder: %s, test_type: %s, name: %s, blob key: %s.",
+ builder, test_type, file.name, file.blob_key)
+
+ return file
+
+ @classmethod
+ def update_file(cls, builder, test_type, blob_info):
+ files = cls.get_files(builder, test_type, blob_info.filename, 1)
+ if not files:
+ return cls.add_file(builder, test_type, blob_info)
+
+ file = files[0]
+ old_blob_info = blobstore.BlobInfo.get(file.blob_key)
+ if old_blob_info:
+ old_blob_info.delete()
+
+ file.builder = builder
+ file.test_type = test_type
+ file.name = blob_info.filename
+ file.blob_key = str(blob_info.key())
+ file.date = datetime.now()
+ file.put()
+
+ logging.info(
+ "File replaced, builder: %s, test_type: %s, name: %s, blob key: %s.",
+ builder, test_type, file.name, file.blob_key)
+
+ return file
+
+ def _delete_all(self):
+ if self.blob_key:
+ blob_info = blobstore.BlobInfo.get(self.blob_key)
+ if blob_info:
+ blob_info.delete()
+
+ self.delete()
diff --git a/WebKitTools/TestResultServer/stylesheets/dashboardfile.css b/WebKitTools/TestResultServer/stylesheets/dashboardfile.css
new file mode 100644
index 0000000..1b0921c
--- /dev/null
+++ b/WebKitTools/TestResultServer/stylesheets/dashboardfile.css
@@ -0,0 +1,30 @@
+body {
+ font-family: Verdana, Helvetica, sans-serif;
+ padding: 0px;
+ color: #444;
+}
+h1 {
+ color: #444;
+ font-size: 14pt;
+ font-style: italic;
+ margin: 0px;
+ padding: 5px;
+}
+table {
+ border-spacing: 0px;
+}
+th {
+ background-color: #AAA;
+ color: white;
+ text-align: left;
+ padding: 5px;
+ font-size: 12pt;
+}
+td {
+ font-size: 11pt;
+ padding: 3px;
+ text-align: left;
+}
+tr:hover {
+ background-color: #EEE;
+}
diff --git a/WebKitTools/TestResultServer/stylesheets/form.css b/WebKitTools/TestResultServer/stylesheets/form.css
new file mode 100644
index 0000000..b8f367d
--- /dev/null
+++ b/WebKitTools/TestResultServer/stylesheets/form.css
@@ -0,0 +1,26 @@
+body {
+ font-family: Verdana;
+ padding: 0px;
+ color: #444;
+}
+h1 {
+ color: #444;
+ font-size: 14pt;
+ font-style: italic;
+ margin: 0px;
+ padding: 5px;
+}
+.label {
+ margin: 1px;
+ padding: 5px;
+ font-size: 11pt;
+ width: 90px;
+}
+.inputtext {
+ font-size: 11pt;
+}
+.button {
+ margin: 1px;
+ padding: 1px;
+ font-size: 11pt;
+}
diff --git a/WebKitTools/TestResultServer/stylesheets/menu.css b/WebKitTools/TestResultServer/stylesheets/menu.css
new file mode 100644
index 0000000..9948605
--- /dev/null
+++ b/WebKitTools/TestResultServer/stylesheets/menu.css
@@ -0,0 +1,28 @@
+body {
+ font-family: Verdana, Helvetica, sans-serif;
+}
+h1 {
+ background-color: #EEE;
+ color: #444;
+ font-size: 14pt;
+ font-style: italic;
+ margin: 0px;
+ padding: 5px;
+}
+ul {
+ margin: 0px;
+ padding: 20px;
+ list-style: none;
+}
+li {
+ padding: 5px;
+}
+li:hover {
+ background-color: #EEE;
+}
+.login {
+ font-size: 8pt;
+ text-align: right;
+ width: 100%;
+}
+
diff --git a/WebKitTools/TestResultServer/stylesheets/testfile.css b/WebKitTools/TestResultServer/stylesheets/testfile.css
new file mode 100644
index 0000000..1b0921c
--- /dev/null
+++ b/WebKitTools/TestResultServer/stylesheets/testfile.css
@@ -0,0 +1,30 @@
+body {
+ font-family: Verdana, Helvetica, sans-serif;
+ padding: 0px;
+ color: #444;
+}
+h1 {
+ color: #444;
+ font-size: 14pt;
+ font-style: italic;
+ margin: 0px;
+ padding: 5px;
+}
+table {
+ border-spacing: 0px;
+}
+th {
+ background-color: #AAA;
+ color: white;
+ text-align: left;
+ padding: 5px;
+ font-size: 12pt;
+}
+td {
+ font-size: 11pt;
+ padding: 3px;
+ text-align: left;
+}
+tr:hover {
+ background-color: #EEE;
+}
diff --git a/WebKitTools/TestResultServer/templates/dashboardfilelist.html b/WebKitTools/TestResultServer/templates/dashboardfilelist.html
new file mode 100644
index 0000000..818cb91
--- /dev/null
+++ b/WebKitTools/TestResultServer/templates/dashboardfilelist.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Dashboard Files</title>
+<link type="text/css" rel="stylesheet" href="/stylesheets/dashboardfile.css" />
+</head>
+<body>
+<h1>Dashboard Files
+</h1>
+<div>
+ <table>
+ <tr>
+ <th>File</th>
+ <th>Date</th>
+ {% if admin %}
+ <th></th>
+ {% endif %}
+ {% for file in files %}
+ <tr>{% if file.name %}
+ <td><a href="/dashboards/{{ file.name }}" >
+ {{ file.name }}
+ </a>
+ </td>
+ <td>{{ file.date|date:"d-M-Y H:i:s" }}
+ </td>
+ {% if admin %}
+ <td><a href="/dashboards/delete?file={{ file.name }}" >
+ Delete
+ </a>
+ </td>
+ {% endif %}
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+</body>
+</html>
diff --git a/WebKitTools/TestResultServer/templates/menu.html b/WebKitTools/TestResultServer/templates/menu.html
new file mode 100644
index 0000000..1ad9f4d
--- /dev/null
+++ b/WebKitTools/TestResultServer/templates/menu.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test Result Server</title>
+<table class=login>
+ <tr>
+ <td>
+ {% if user_email %}
+ <span>{{ user_email }}</span>
+ {% endif %}
+ <span><a href="{{ login_url }}">{{ login_text }}</a></span>
+ </td>
+ </tr>
+</table>
+<link type="text/css" rel="stylesheet" href="/stylesheets/menu.css" />
+</head>
+<body>
+<h1>Test Result Server</h1>
+<div>
+ <ul>{% for title,link in menu %}
+ <li>
+ <a href="{{ link }}" >{{ title }}</a>
+ </li>{% endfor %}
+ </ul>
+</div>
+</body>
+</html>
diff --git a/WebKitTools/TestResultServer/templates/showfilelist.html b/WebKitTools/TestResultServer/templates/showfilelist.html
new file mode 100644
index 0000000..fa72b7f
--- /dev/null
+++ b/WebKitTools/TestResultServer/templates/showfilelist.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test Results</title>
+<link type="text/css" rel="stylesheet" href="/stylesheets/testfile.css" />
+</head>
+<body>
+<h1>Test Results
+{% if builder or test_type or name %}
+- {{ builder }} {{test_type }} {{ name }}
+{% endif %}
+</h1>
+<div>
+ <table>
+ <tr>
+ <th>Builder</th>
+ <th>Test Type</th>
+ <th>File</th>
+ <th>Date</th>
+ {% if admin %}
+ <th></th>
+ {% endif %}
+ {% for file in files %}
+ <tr>{% if file.builder and file.name %}
+ <td><a href="/testfile?builder={{ file.builder }}" >
+ {{ file.builder }}
+ </a>
+ </td>
+ <td>{% if file.test_type %}
+ <a href="/testfile?testtype={{ file.test_type }}" >
+ {{ file.test_type }}
+ </a>
+ {% endif %}
+ </td>
+ <td><a href="/testfile?builder={{ file.builder }}&name={{ file.name }}" >
+ {{ file.name }}
+ </a>
+ </td>
+ <td>{{ file.date|date:"d-M-Y H:i:s" }}
+ </td>
+ {% if admin %}
+ <td><a href="/testfile/delete?key={{ file.key }}&builder={{ builder }}&name={{ name }}" >
+ Delete
+ </a>
+ </td>
+ {% endif %}
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+</body>
+</html>
diff --git a/WebKitTools/TestResultServer/templates/uploadform.html b/WebKitTools/TestResultServer/templates/uploadform.html
new file mode 100644
index 0000000..933f9f5
--- /dev/null
+++ b/WebKitTools/TestResultServer/templates/uploadform.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Upload Test Result File</title>
+<link type="text/css" rel="stylesheet" href="/stylesheets/form.css" />
+</head>
+<body>
+<h1>Upload Test Result File</h1>
+<form id="uploadForm" name="test_result_upload" accept="text/html" action="{{ upload_url }}" enctype="multipart/form-data" method="post">
+ <br>
+ <table>
+ <tr>
+ <td class=label><label>Builder:</label></td>
+ <td><input class=inputtext type="text" name="builder" value="Webkit"/></td>
+ </tr>
+ <tr>
+ <td class=label><label>Test Type:</label></td>
+ <td><input class=inputtext type="text" name="testtype" value=""/></td>
+ </tr>
+ </table>
+ <div><input class=button type="file" name="file" multiple></div>
+ <br>
+ <div><input class=button type="submit" value="Upload"></div>
+</form>
+</body>
+</html>
diff --git a/WebKitTools/WinLauncher/WinLauncher.h b/WebKitTools/WinLauncher/WinLauncher.h
index adc2b17..6488285 100644
--- a/WebKitTools/WinLauncher/WinLauncher.h
+++ b/WebKitTools/WinLauncher/WinLauncher.h
@@ -1,115 +1,115 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#pragma once
-
-#include "resource.h"
-#include <WebKit/WebKit.h>
-
-class WinLauncherWebHost : public IWebFrameLoadDelegate
-{
-public:
- WinLauncherWebHost() : m_refCount(1) {}
-
- // IUnknown
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
- virtual ULONG STDMETHODCALLTYPE AddRef(void);
- virtual ULONG STDMETHODCALLTYPE Release(void);
-
- // IWebFrameLoadDelegate
- virtual HRESULT STDMETHODCALLTYPE didStartProvisionalLoadForFrame(
- /* [in] */ IWebView* webView,
- /* [in] */ IWebFrame* /*frame*/) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didReceiveServerRedirectForProvisionalLoadForFrame(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didFailProvisionalLoadWithError(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebError *error,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didCommitLoadForFrame(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebFrame *frame) { return updateAddressBar(webView); }
-
- virtual HRESULT STDMETHODCALLTYPE didReceiveTitle(
- /* [in] */ IWebView *webView,
- /* [in] */ BSTR title,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didReceiveIcon(
- /* [in] */ IWebView *webView,
- /* [in] */ OLE_HANDLE hBitmap,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didFinishLoadForFrame(
- /* [in] */ IWebView* webView,
- /* [in] */ IWebFrame* /*frame*/) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didFailLoadWithError(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebError *error,
- /* [in] */ IWebFrame *forFrame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didChangeLocationWithinPageForFrame(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE willPerformClientRedirectToURL(
- /* [in] */ IWebView *webView,
- /* [in] */ BSTR url,
- /* [in] */ double delaySeconds,
- /* [in] */ DATE fireDate,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE didCancelClientRedirectForFrame(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE willCloseFrame(
- /* [in] */ IWebView *webView,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- virtual /* [local] */ HRESULT STDMETHODCALLTYPE windowScriptObjectAvailable(
- /* [in] */ IWebView *webView,
- /* [in] */ JSContextRef context,
- /* [in] */ JSObjectRef windowScriptObject) { return S_OK; }
-
- virtual /* [local] */ HRESULT STDMETHODCALLTYPE didClearWindowObject(
- /* [in] */ IWebView *webView,
- /* [in] */ JSContextRef context,
- /* [in] */ JSObjectRef windowScriptObject,
- /* [in] */ IWebFrame *frame) { return S_OK; }
-
- // WinLauncherWebHost
-
-protected:
- HRESULT updateAddressBar(IWebView* webView);
-
-protected:
- ULONG m_refCount;
-};
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "resource.h"
+#include <WebKit/WebKit.h>
+
+class WinLauncherWebHost : public IWebFrameLoadDelegate
+{
+public:
+ WinLauncherWebHost() : m_refCount(1) {}
+
+ // IUnknown
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
+ virtual ULONG STDMETHODCALLTYPE AddRef(void);
+ virtual ULONG STDMETHODCALLTYPE Release(void);
+
+ // IWebFrameLoadDelegate
+ virtual HRESULT STDMETHODCALLTYPE didStartProvisionalLoadForFrame(
+ /* [in] */ IWebView* webView,
+ /* [in] */ IWebFrame* /*frame*/) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didReceiveServerRedirectForProvisionalLoadForFrame(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didFailProvisionalLoadWithError(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebError *error,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didCommitLoadForFrame(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebFrame *frame) { return updateAddressBar(webView); }
+
+ virtual HRESULT STDMETHODCALLTYPE didReceiveTitle(
+ /* [in] */ IWebView *webView,
+ /* [in] */ BSTR title,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didReceiveIcon(
+ /* [in] */ IWebView *webView,
+ /* [in] */ OLE_HANDLE hBitmap,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didFinishLoadForFrame(
+ /* [in] */ IWebView* webView,
+ /* [in] */ IWebFrame* /*frame*/) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didFailLoadWithError(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebError *error,
+ /* [in] */ IWebFrame *forFrame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didChangeLocationWithinPageForFrame(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE willPerformClientRedirectToURL(
+ /* [in] */ IWebView *webView,
+ /* [in] */ BSTR url,
+ /* [in] */ double delaySeconds,
+ /* [in] */ DATE fireDate,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE didCancelClientRedirectForFrame(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual HRESULT STDMETHODCALLTYPE willCloseFrame(
+ /* [in] */ IWebView *webView,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ virtual /* [local] */ HRESULT STDMETHODCALLTYPE windowScriptObjectAvailable(
+ /* [in] */ IWebView *webView,
+ /* [in] */ JSContextRef context,
+ /* [in] */ JSObjectRef windowScriptObject) { return S_OK; }
+
+ virtual /* [local] */ HRESULT STDMETHODCALLTYPE didClearWindowObject(
+ /* [in] */ IWebView *webView,
+ /* [in] */ JSContextRef context,
+ /* [in] */ JSObjectRef windowScriptObject,
+ /* [in] */ IWebFrame *frame) { return S_OK; }
+
+ // WinLauncherWebHost
+
+protected:
+ HRESULT updateAddressBar(IWebView* webView);
+
+protected:
+ ULONG m_refCount;
+};
diff --git a/WebKitTools/gdb/webcore.py b/WebKitTools/gdb/webcore.py
new file mode 100644
index 0000000..83886f8
--- /dev/null
+++ b/WebKitTools/gdb/webcore.py
@@ -0,0 +1,195 @@
+# Copyright (C) 2010, 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.
+
+"""GDB support for WebKit types.
+
+Add this to your gdb by amending your ~/.gdbinit as follows:
+ python
+ import sys
+ sys.path.insert(0, "/path/to/tools/gdb/")
+ import webcore
+"""
+
+import gdb
+import struct
+
+def ustring_to_string(ptr, length=None):
+ """Convert a pointer to UTF-16 data into a Python Unicode string.
+
+ ptr and length are both gdb.Value objects.
+ If length is unspecified, will guess at the length."""
+ extra = ''
+ if length is None:
+ # Try to guess at the length.
+ for i in xrange(0, 2048):
+ if int((ptr + i).dereference()) == 0:
+ length = i
+ break
+ if length is None:
+ length = 256
+ extra = u' (no trailing NUL found)'
+ else:
+ length = int(length)
+
+ char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
+ string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace')
+
+ return string + extra
+
+
+class StringPrinter(object):
+ "Shared code between different string-printing classes"
+ def __init__(self, val):
+ self.val = val
+
+ def display_hint(self):
+ return 'string'
+
+
+class UCharStringPrinter(StringPrinter):
+ "Print a UChar*; we must guess at the length"
+ def to_string(self):
+ return ustring_to_string(self.val)
+
+
+class WebCoreAtomicStringPrinter(StringPrinter):
+ "Print a WebCore::AtomicString"
+ def to_string(self):
+ return self.val['m_string']
+
+
+class WebCoreStringPrinter(StringPrinter):
+ "Print a WebCore::String"
+ def get_length(self):
+ if not self.val['m_impl']['m_ptr']:
+ return 0
+ return self.val['m_impl']['m_ptr']['m_length']
+
+ def to_string(self):
+ if self.get_length() == 0:
+ return '(null)'
+
+ return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
+ self.get_length())
+
+
+class WebCoreQualifiedNamePrinter(StringPrinter):
+ "Print a WebCore::QualifiedName"
+
+ def __init__(self, val):
+ super(WebCoreQualifiedNamePrinter, self).__init__(val)
+ self.prefix_length = 0
+ self.length = 0
+ if self.val['m_impl']:
+ self.prefix_printer = WebCoreStringPrinter(
+ self.val['m_impl']['m_prefix']['m_string'])
+ self.local_name_printer = WebCoreStringPrinter(
+ self.val['m_impl']['m_localName']['m_string'])
+ self.prefix_length = self.prefix_printer.get_length()
+ if self.prefix_length > 0:
+ self.length = (self.prefix_length + 1 +
+ self.local_name_printer.get_length())
+ else:
+ self.length = self.local_name_printer.get_length()
+
+ def get_length(self):
+ return self.length
+
+ def to_string(self):
+ if self.get_length() == 0:
+ return "(null)"
+ else:
+ if self.prefix_length > 0:
+ return (self.prefix_printer.to_string() + ":" +
+ self.local_name_printer.to_string())
+ else:
+ return self.local_name_printer.to_string()
+
+
+
+def lookup_function(val):
+ """Function used to load pretty printers; will be passed to GDB."""
+ lookup_tag = val.type.tag
+ printers = {
+ "WebCore::AtomicString": WebCoreAtomicStringPrinter,
+ "WebCore::String": WebCoreStringPrinter,
+ "WebCore::QualifiedName": WebCoreQualifiedNamePrinter,
+ }
+ name = val.type.tag
+ if name in printers:
+ return printers[name](val)
+
+ if val.type.code == gdb.TYPE_CODE_PTR:
+ name = str(val.type.target().unqualified())
+ if name == 'UChar':
+ return UCharStringPrinter(val)
+
+ return None
+
+
+gdb.pretty_printers.append(lookup_function)
+
+
+
+class PrintPathToRootCommand(gdb.Command):
+ """Command for printing WebKit Node trees.
+Usage: printpathtoroot variable_name
+"""
+
+ def __init__(self):
+ super(PrintPathToRootCommand, self).__init__("printpathtoroot",
+ gdb.COMMAND_SUPPORT,
+ gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, from_tty):
+ element_type = gdb.lookup_type('WebCore::Element')
+ node_type = gdb.lookup_type('WebCore::Node')
+ frame = gdb.selected_frame()
+ try:
+ val = gdb.Frame.read_var(frame, arg)
+ except:
+ print "No such variable, or invalid type"
+ return
+
+ target_type = str(val.type.target().strip_typedefs())
+ if target_type == str(node_type):
+ stack = []
+ while val:
+ stack.append([val,
+ val.cast(element_type.pointer()).dereference()['m_tagName']])
+ val = val.dereference()['m_parent']
+
+ padding = ''
+ while len(stack) > 0:
+ pair = stack.pop()
+ print padding, pair[1], pair[0]
+ padding = padding + ' '
+ else:
+ print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
+
+PrintPathToRootCommand()
diff --git a/WebKitTools/iExploder/htdocs/cssproperties.in b/WebKitTools/iExploder/htdocs/cssproperties.in
index 52ccdbe..fc2ee50 100644
--- a/WebKitTools/iExploder/htdocs/cssproperties.in
+++ b/WebKitTools/iExploder/htdocs/cssproperties.in
@@ -1,8 +1,9 @@
-# From WebKit svn r53119 (WebCore/css/CSSPropertyNames.in)
+# From WebKit svn r55658 (WebCore/css/CSSPropertyNames.in)
-webkit-animation
-webkit-animation-delay
-webkit-animation-direction
-webkit-animation-duration
+-webkit-animation-fill-mode
-webkit-animation-iteration-count
-webkit-animation-name
-webkit-animation-play-state
diff --git a/WebKitTools/iExploder/htdocs/htmlattrs.in b/WebKitTools/iExploder/htdocs/htmlattrs.in
index 2238427..56b4050 100644
--- a/WebKitTools/iExploder/htdocs/htmlattrs.in
+++ b/WebKitTools/iExploder/htdocs/htmlattrs.in
@@ -1,4 +1,4 @@
-# From WebKit svn r53119 (WebCore/html/HTMLAttributeNames.in)
+# From WebKit svn r55658 (WebCore/html/HTMLAttributeNames.in)
abbr
accept
accept_charset
@@ -19,6 +19,7 @@ aria-dropeffect
aria-expanded
aria-flowto
aria-grabbed
+aria-haspopup
aria-hidden
aria-label
aria-labeledby
@@ -37,7 +38,6 @@ aria-valuemax
aria-valuemin
aria-valuenow
aria-valuetext
-autobuffer
autocomplete
autofocus
autoplay
@@ -83,6 +83,7 @@ disabled
draggable
enctype
end
+event
expanded
face
focused
@@ -198,6 +199,7 @@ onstorage
onsubmit
onsuspend
ontimeupdate
+ontouchcancel
ontouchend
ontouchmove
ontouchstart
@@ -216,6 +218,7 @@ playcount
pluginurl
poster
precision
+preload
primary
profile
progress
@@ -268,6 +271,9 @@ vspace
width
wrap
+# Removed from WebKit between r53119 and r56558
+autobuffer
+
# Removed from WebKit between r14011 and r53119
left
pagex
diff --git a/WebKitTools/iExploder/htdocs/htmltags.in b/WebKitTools/iExploder/htdocs/htmltags.in
index b47dd9c..f01d2a7 100644
--- a/WebKitTools/iExploder/htdocs/htmltags.in
+++ b/WebKitTools/iExploder/htdocs/htmltags.in
@@ -1,4 +1,4 @@
-# From WebKit svn r53119 (WebCore/html/HTMLTagNames.in)
+# From WebKit svn r55658 (WebCore/html/HTMLTagNames.in)
a
abbr
acronym
diff --git a/WebKitTools/wx/browser/browser.cpp b/WebKitTools/wx/browser/browser.cpp
index df701bb..95c39d4 100644
--- a/WebKitTools/wx/browser/browser.cpp
+++ b/WebKitTools/wx/browser/browser.cpp
@@ -61,7 +61,7 @@ bool MyApp::OnInit()
#if __WXMSW__ || __WXMAC__
settings.SetPluginsEnabled(true);
#endif
- settings.SetDatabasesEnabled(true);
+ frame->webview->SetDatabasesEnabled(true);
settings.SetEditableLinkBehavior(wxEditableLinkOnlyLiveWithShiftKey);
frame->CentreOnScreen();
frame->Show(true);
diff --git a/WebKitTools/wx/build/settings.py b/WebKitTools/wx/build/settings.py
index d3b7b2c..baf9657 100644
--- a/WebKitTools/wx/build/settings.py
+++ b/WebKitTools/wx/build/settings.py
@@ -60,7 +60,7 @@ common_libpaths = []
common_frameworks = []
ports = [
- 'CF',
+ 'Brew',
'Chromium',
'Gtk',
'Haiku',
@@ -90,6 +90,7 @@ jscore_dirs = [
'profiler',
'runtime',
'wtf',
+ 'wtf/text',
'wtf/unicode',
'wtf/unicode/icu',
'yarr',
@@ -120,7 +121,8 @@ webcore_dirs = [
'page/animation',
'platform',
'platform/animation',
- 'platform/graphics',
+ 'platform/graphics',
+ 'platform/graphics/filters',
'platform/graphics/transforms',
'platform/image-decoders',
'platform/image-decoders/bmp',
@@ -135,7 +137,11 @@ webcore_dirs = [
'plugins',
'rendering',
'rendering/style',
- 'storage',
+ 'storage',
+ 'svg',
+ 'svg/animation',
+ 'svg/graphics',
+ 'svg/graphics/filters',
'websockets',
'xml'
]
@@ -168,7 +174,7 @@ if building_on_win32:
create_hash_table = get_output('cygpath --unix "%s"' % create_hash_table)
os.environ['CREATE_HASH_TABLE'] = create_hash_table
-feature_defines = ['ENABLE_DATABASE', 'ENABLE_XSLT', 'ENABLE_JAVASCRIPT_DEBUGGER']
+feature_defines = ['ENABLE_DATABASE', 'ENABLE_XSLT', 'ENABLE_JAVASCRIPT_DEBUGGER', 'ENABLE_SVG', 'ENABLE_SVG_USE', 'ENABLE_FILTERS', 'ENABLE_SVG_FONTS', 'ENABLE_SVG_ANIMATION', 'ENABLE_SVG_AS_IMAGE']
msvc_version = 'msvc2008'