diff options
author | Steve Block <steveblock@google.com> | 2009-12-15 10:12:09 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-12-17 17:41:10 +0000 |
commit | 643ca7872b450ea4efacab6188849e5aac2ba161 (patch) | |
tree | 6982576c228bcd1a7efe98afed544d840751094c /WebKitTools | |
parent | d026980fde6eb3b01c1fe49441174e89cd1be298 (diff) | |
download | external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.zip external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.gz external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.bz2 |
Merge webkit.org at r51976 : Initial merge by git.
Change-Id: Ib0e7e2f0fb4bee5a186610272edf3186f0986b43
Diffstat (limited to 'WebKitTools')
140 files changed, 12115 insertions, 1902 deletions
diff --git a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json index 2b8faf6..69ff238 100644 --- a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json +++ b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/config.json @@ -22,10 +22,14 @@ { "name": "apple-windows-4", "platform": "win"}, { "name": "gtk-linux-slave-1", "platform": "gtk"}, + { "name": "gtk-linux-slave-2", "platform": "gtk"}, + { "name": "gtk-linux-slave-3", "platform": "gtk"}, { "name": "szeged-linux-1", "platform": "qt"}, - { "name": "google-slave-1", "platform": "chromium-win" } + { "name": "google-windows-1", "platform": "chromium" }, + { "name": "google-mac-1", "platform": "chromium" }, + { "name": "google-linux-1", "platform": "chromium" } ], "builders": [ { "name": "Tiger Intel Release", "type": "BuildAndTest", "builddir": "tiger-intel-release", @@ -93,14 +97,34 @@ "slavenames": ["gtk-linux-slave-1"] }, { + "name": "GTK Linux 32-bit Debug", "type": "BuildAndTest", "builddir": "gtk-linux-32-debug", + "platform": "gtk", "configuration": "debug", "architectures": ["i386"], + "slavenames": ["gtk-linux-slave-2"] + }, + { + "name": "GTK Linux 64-bit Debug", "type": "BuildAndTest", "builddir": "gtk-linux-64-debug", + "platform": "gtk", "configuration": "debug", "architectures": ["x86_64"], + "slavenames": ["gtk-linux-slave-3"] + }, + { "name": "Qt Linux Release", "type": "BuildAndTest", "builddir": "qt-linux-release", "platform": "qt", "configuration": "release", "architectures": ["i386"], "slavenames": ["szeged-linux-1"] }, { - "name": "Chromium Win Release", "type": "ChromiumBuild", "builddir": "chromium-win-release", - "platform": "chromium-win", "configuration": "release", "architectures": ["i386"], - "slavenames": ["google-slave-1"] + "name": "Chromium Win Release", "type": "Build", "builddir": "chromium-win-release", + "platform": "chromium", "configuration": "release", "architectures": ["i386"], + "slavenames": ["google-windows-1"] + }, + { + "name": "Chromium Mac Release", "type": "Build", "builddir": "chromium-mac-release", + "platform": "chromium", "configuration": "release", "architectures": ["i386"], + "slavenames": ["google-mac-1"] + }, + { + "name": "Chromium Linux Release", "type": "Build", "builddir": "chromium-linux-release", + "platform": "chromium", "configuration": "release", "architectures": ["i386"], + "slavenames": ["google-linux-1"] } ], @@ -108,8 +132,9 @@ "builderNames": ["Tiger Intel Release", "Leopard Intel Release (Build)", "Leopard Intel Debug (Build)", "SnowLeopard Intel Release (Build)", "SnowLeopard Intel Leaks", "Windows Release (Build)", "Windows Debug (Build)", - "GTK Linux Release", "Qt Linux Release", - "Chromium Win Release"] + "GTK Linux Release", "GTK Linux 32-bit Debug", "GTK Linux 64-bit Debug", + "Qt Linux Release", + "Chromium Win Release", "Chromium Mac Release", "Chromium Linux Release"] }, { "type": "Triggerable", "name": "leopard-intel-release-tests", "builderNames": ["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 6219b0d..cd81108 100644 --- a/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg +++ b/WebKitTools/BuildSlaveSupport/build.webkit.org-config/master.cfg @@ -48,63 +48,22 @@ class CheckOutSource(source.SVN): source.SVN.__init__(self, baseURL=self.baseURL, defaultBranch="trunk", mode=self.mode, *args, **kwargs) -# FIXME: Remove this step once Chromium WebKit port build system is decoupled from -# Chromium (https://bugs.webkit.org/show_bug.cgi?id=28396) -class UpdateChromiumSource(shell.ShellCommand): - command = ["gclient", "sync"] - name = "update-chromium" - description = ["updating chromium source"] - descriptionDone = ["updated"] - haltOnFailure = True - - def createSummary(self, log): - scraper = re.compile(r"^________ running '[^\n]+third_party[/\\]WebKit[^\n]+$\n(?:^[UA]\W+[^\n]+$\n)*^(?:Updated to|At) revision (\d+)", re.DOTALL | re.MULTILINE) - revisions = scraper.findall(log.getText()) - gotRevision = "??" # This matches SVN unknown revision response. - if len(revisions): - gotRevision = "r%s" % revisions[-1] - self.descriptionDone = ["updated", gotRevision] - - def start(self): - os = self.getProperty("fullPlatform").split('-')[1] - if os == "win": - self.setCommand(["gclient.bat", "sync"]) - revision = self.getProperty("revision") - if revision: - command = self.command[:] - command.append("--revision=src/third_party/WebKit@%d" % revision) - self.setCommand(command) - return shell.ShellCommand.start(self) - - -# FIXME: Remove this step once Chromium WebKit port build system is decoupled from -# Chromium (https://bugs.webkit.org/show_bug.cgi?id=28396) -class CompileChromiumWebKit(shell.ShellCommand): - command = ["python", "../../../scripts/slave/compile.py"] - name = "build-chromium" - description = ["compiling"] - descriptionDone = ["compiled"] - haltOnFailure = True - - def start(self): - os = self.getProperty("fullPlatform").split('-')[1] - command = self.command[:] - if os == "win": - command.extend(["--solution=webkit.sln", "--build-dir=src\\webkit", "--", "/project", "webcore"]) - elif os == "mac": - command.extend(["--solution=__solution__", "--build-dir=src/build", "--", "-project", "../webkit/webkit.xcodeproj", "-target", "webcore"]) - self.setCommand(command) - return shell.ShellCommand.start(self) - - class InstallWin32Dependencies(shell.Compile): description = ["installing dependencies"] descriptionDone = ["installed dependencies"] command = ["perl", "./WebKitTools/Scripts/update-webkit-auxiliary-libs"] +class InstallChromiumDependencies(shell.ShellCommand): + name = "gclient" + description = ["updating chromium dependencies"] + descriptionDone = ["updated chromium dependencies"] + command = ["perl", "./WebKitTools/Scripts/update-webkit-chromium"] + haltOnFailure = True + + def appendCustomBuildFlags(step, platform): - if platform in ('gtk', 'wx', 'qt'): + if platform in ('gtk', 'wx', 'qt', 'chromium'): step.setCommand(step.command + ['--' + platform]) @@ -215,7 +174,7 @@ class RunWebKitTests(shell.Test): descriptionDone = ["layout-tests"] command = ["perl", "./WebKitTools/Scripts/run-webkit-tests", "--no-launch-safari", "--no-new-test-results", "--no-sample-on-timeout", "--results-directory", "layout-test-results", "--use-remote-links-to-tests", - WithProperties("--%(configuration)s")] + WithProperties("--%(configuration)s"), "--exit-after-n-failures", "20"] def __init__(self, skipBuild=False, *args, **kwargs): self.skipBuild = skipBuild @@ -240,6 +199,8 @@ class RunWebKitTests(shell.Test): incorrectLayoutLines.append(line) elif line.startswith("WARNING:") and line.find(' leak') >= 0: incorrectLayoutLines.append(line.replace('WARNING: ', '')) + elif line.find('Exiting early') >= 0: + incorrectLayoutLines.append(line) # FIXME: Detect and summarize leaks of RefCounted objects @@ -318,23 +279,17 @@ class Factory(factory.BuildFactory): self.addStep(CheckOutSource) if platform == "win": self.addStep(InstallWin32Dependencies) - -# FIXME: Remove this factory once Chromium WebKit port build system is decoupled from -# Chromium (https://bugs.webkit.org/show_bug.cgi?id=28396) -class ChromiumBuildFactory(factory.BuildFactory): - def __init__(self, platform, configuration, architectures): - factory.BuildFactory.__init__(self) - self.addStep(ConfigureBuild, platform=platform, configuration=configuration, architecture=" ".join(architectures), buildOnly=True) - self.addStep(UpdateChromiumSource) - self.addStep(CompileChromiumWebKit) + if platform == "chromium": + self.addStep(InstallChromiumDependencies) class BuildFactory(Factory): - def __init__(self, platform, configuration, architectures, triggers): + def __init__(self, platform, configuration, architectures, triggers=None): Factory.__init__(self, platform, configuration, architectures, True) self.addStep(CompileWebKit) - self.addStep(ArchiveBuiltProduct) - self.addStep(UploadBuiltProduct) - self.addStep(trigger.Trigger, schedulerNames=triggers) + if triggers: + self.addStep(ArchiveBuiltProduct) + self.addStep(UploadBuiltProduct) + self.addStep(trigger.Trigger, schedulerNames=triggers) class TestFactory(Factory): def __init__(self, platform, configuration, architectures): diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog index 7dcbf6c..c55009d 100644 --- a/WebKitTools/ChangeLog +++ b/WebKitTools/ChangeLog @@ -1,3 +1,3947 @@ +2009-12-10 Eric Seidel <eric@webkit.org> + + No review, just updating unit tests to match recent checkins. + + * Scripts/modules/mock_bugzillatool.py: + - Add missing red_core_builders_names method causing exception. + * Scripts/modules/workqueue_unittest.py: + - processutils is dead, use executive.py instead. + +2009-12-10 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Move run_command onto Executive to make code which uses run_command testable + https://bugs.webkit.org/show_bug.cgi?id=32396 + + * Scripts/modules/executive.py: + - Move run_command and error handlers onto Executive. + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + +2009-12-09 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool needs a command to list patches needing cq+ + https://bugs.webkit.org/show_bug.cgi?id=32351 + + * Scripts/modules/bugzilla.py: + - Parse attacher_email from attachment xml. + * Scripts/modules/bugzilla_unittest.py: + - Test new attacher_email parsing. + * Scripts/modules/commands/queries.py: + - Add PatchesToCommitQueue + * Scripts/modules/commands/queries_unittest.py: + - Tests for PatchesToCommitQueue + * Scripts/modules/mock_bugzillatool.py: + - Add necessary mock methods for running PatchesToCommitQueue + +2009-12-10 Adam Barth <abarth@webkit.org> + + Unreviewed. Turns out every StepSequence command needs a --quiet + option. + + * Scripts/modules/stepsequence.py: + +2009-12-10 Eric Z. Ayers <zundel@google.com> + + Reviewed by Pavel Feldman. + + Implements displayWebView() to force an invalidation and repaint. + This fixes a problem running the timeline-paint.html unit test. + + https://bugs.webkit.org/show_bug.cgi?id=31729 + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (displayWebView): + +2009-12-10 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Convert more commands to StepSequences + https://bugs.webkit.org/show_bug.cgi?id=32362 + + We should eventually convert all the commands, but I'm starting with + the easy ones. + + * Scripts/modules/commands/download.py: + +2009-12-10 Xan Lopez <xlopez@igalia.com> + + Reviewed by Jan Alonzo. + + [GTK] editing/selection/shrink-selection-after-shift-pagedown.html failing + https://bugs.webkit.org/show_bug.cgi?id=31103 + + Give focus to the webviews when we create them, since some tests expect this. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (main): + +2009-12-10 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool "builders are red" error should tell you which builders + https://bugs.webkit.org/show_bug.cgi?id=32211 + + * Scripts/modules/buildsteps.py: + +2009-12-09 Marwan Al Jubeh <marwan.aljubeh@gmail.com> + + Reviewed by Adam Roben. + + Fixes: https://bugs.webkit.org/show_bug.cgi?id=31228 + Set the WebKitOutputDir, WebKitLibrariesDir and Cygwin environment variables automatically + in Windows as part of running update_webkit. + + * Scripts/update-webkit: + - Run setupAppleWinEnv() on Apple's Windows port. + * Scripts/webkitdirs.pm: + - Added functions that return the source directory, libraries directory and default build directory on Windows. + - Added isWindowsNT() which tests if the current Windows version is from the Windows NT family. + - Implemented setupAppleWinEnv() which sets the environment variables WebKitOutputDir, WebKitLibrariesDir + and Cygwin to their desired values. + +2009-12-09 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + exception thrown when running apply-patches + https://bugs.webkit.org/show_bug.cgi?id=32344 + + The update step now takes a port option. Once we finish the Steps + refactoring, we won't have to worry about this kind of bug again. + + * Scripts/modules/commands/download.py: + +2009-12-09 Adam Barth <abarth@webkit.org> + + Unreviewed. Fix ScriptError includes. I don't understand why these + didn't throw during unit testing... + + * Scripts/modules/commands/download.py: + * Scripts/modules/logging_unittest.py: + * Scripts/modules/workqueue.py: + +2009-12-09 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Remove unused PatchCollection class + https://bugs.webkit.org/show_bug.cgi?id=32312 + + It's dead code. + + * Scripts/modules/patchcollection.py: + * Scripts/modules/patchcollection_unittest.py: Removed. + +2009-12-09 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. CheckStyle needs a --no-upate option. + + * Scripts/modules/commands/download.py: + +2009-12-09 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Convert Build to use Sequence + https://bugs.webkit.org/show_bug.cgi?id=32310 + + So much prettier. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/stepsequence.py: Added. + +2009-12-09 Adam Barth <abarth@webkit.org> + + Add missing file. + + * Scripts/modules/executive.py: Added. + +2009-12-09 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. + + * Scripts/modules/landingsequence.py: + +2009-12-09 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Implement abstract Steps + https://bugs.webkit.org/show_bug.cgi?id=32212 + + This is a fairly disruptive change that refactors how we build + commands. Instead of using a landing sequence, we can now assemble a + sequence of steps directly. We still use the landing sequence in the + interim, but this will be removed soon. + + * Scripts/bugzilla-tool: + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/commands/early_warning_system.py: + * Scripts/modules/commands/queues.py: + * Scripts/modules/commands/queues_unittest.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/mock_bugzillatool.py: + * Scripts/modules/processutils.py: Removed. + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + * Scripts/modules/webkitport.py: + +2009-12-08 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + run_command and ScriptError should move into processutils.py + https://bugs.webkit.org/show_bug.cgi?id=32305 + + Turns out there are a zillion callers to run_command. + + * Scripts/modules/commands/download.py: + * Scripts/modules/commands/early_warning_system.py: + * Scripts/modules/commands/queues.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/logging_unittest.py: + * Scripts/modules/processutils.py: + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + * Scripts/modules/workqueue.py: + * Scripts/modules/workqueue_unittest.py: + +2009-12-08 Kevin Watters <kevinwatters@gmail.com> + + Reviewed by Kevin Ollivier. + + [wx] Mac plugins support. + + https://bugs.webkit.org/show_bug.cgi?id=32236 + + * wx/browser/wscript: + +2009-12-08 David Levin <levin@chromium.org> + + Reviewed by Adam Barth. + + [check-webkit-style] False positive for DEFINE_OPCODE(op_jtrue) { + https://bugs.webkit.org/show_bug.cgi?id=32193 + + * Scripts/modules/cpp_style.py: For the function { rule, if there is space + at the beginning of line, ignore lines which then have a macro. + * Scripts/modules/cpp_style_unittest.py: Add tests to verify the + new behavior. + +2009-12-08 Dmitry Titov <dimich@chromium.org> + + Rubber-stamped by David Levin. + + Revert and reopen "Add asserts to RefCounted to make sure ref/deref happens on the right thread." + It may have caused massive increase of reported leaks on the bots. + https://bugs.webkit.org/show_bug.cgi?id=31639 + + * DumpRenderTree/ForwardingHeaders/wtf/ThreadVerifier.h: Removed. + +2009-12-08 Dmitry Titov <dimich@chromium.org> + + Reviewed by Darin Adler. + + Add asserts to RefCounted to make sure ref/deref happens on the right thread. + https://bugs.webkit.org/show_bug.cgi?id=31639 + + * DumpRenderTree/ForwardingHeaders/wtf/ThreadVerifier.h: Added. + +2009-12-08 David Levin <levin@chromium.org> + + Reviewed by Adam Barth. + + [check-webkit-style] False positive for camel case of JSC op codes + https://bugs.webkit.org/show_bug.cgi?id=32192 + + * Scripts/modules/cpp_style.py: Added an exception for the JSC op + code functions and const_iterator as well since I noticed a false + positive there when testing the fix. + * Scripts/modules/cpp_style_unittest.py: Added tests for these + changes. + +2009-12-08 Nikolas Zimmermann <nzimmermann@rim.com> + + Rubber-stamped by Maciej Stachowiak. + + Turn on (SVG) Filters for Win. + https://bugs.webkit.org/show_bug.cgi?id=32224 + + * Scripts/webkitdirs.pm: + +2009-12-08 Nikolas Zimmermann <nzimmermann@rim.com> + + Rubber-stamped by Maciej Stachowiak. + + Turn on (SVG) Filters for Gtk. + https://bugs.webkit.org/show_bug.cgi?id=32224 + + * Scripts/build-webkit: + +2009-12-07 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by David Levin. + + [check-webkit-style] S_OK is a fine identifier + https://bugs.webkit.org/show_bug.cgi?id=32225 + + * Scripts/modules/cpp_style.py: + * Scripts/modules/cpp_style_unittest.py: + +2009-12-07 Nikolas Zimmermann <nzimmermann@rim.com> + + Rubber-stamped by Maciej Stachowiak. + + Turn on (SVG) Filters for Qt. + https://bugs.webkit.org/show_bug.cgi?id=32224 + + * Scripts/build-webkit: + +2009-12-07 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Alexey Proskuryakov. + + Make run-webkit-websocketserver log to stderr rather than to a file. + https://bugs.webkit.org/show_bug.cgi?id=32234 + + * Scripts/run-webkit-websocketserver: + +2009-12-07 Dmitry Titov <dimich@chromium.org> + + Rubber-stamped by Darin Adler. + + Remove ENABLE_SHARED_SCRIPT flags + https://bugs.webkit.org/show_bug.cgi?id=32245 + This patch was obtained by "git revert" command and then un-reverting of ChangeLog files. + + * Scripts/build-webkit: + +2009-12-07 Simon Fraser <simon.fraser@apple.com> + + Reviewed by Darin Adler. + + <rdar://problem/7450481> One compositing test keeps DRT in "compositing mode", breaks repaint tests + + The counter that WebView used to keep track of the number of enclosed WebHTMLViews using + accelerated compositing was hard to manage, and maintained incorrectly in a number of cases. + This caused one compositing test make DumpRenderTree think that all subsequent tests + were compositing too. + + Replace this counter with notifications, which are only fired if a client (DRT) requests them. The + notification informs the client that a WebHTMLView entered compositing mode (or an already- + compositing WebHTML was added); it does not say when a view becomes uncomposited, or all + compositing subviews were removed, since this is tricky to get right. + + DumpRenderTreeWindow listens for this notification, and uses it to turn on window + autodisplay, which is necessary to kick-start Core Animation rendering and animations. + We ensure that window autodisplay is turned off before every test. + + * DumpRenderTree/mac/DumpRenderTree.mm: + (createWebViewAndOffscreenWindow): + (resetWebViewToConsistentStateBeforeTesting): + * DumpRenderTree/mac/DumpRenderTreeWindow.h: + * DumpRenderTree/mac/DumpRenderTreeWindow.mm: + (-[DumpRenderTreeWindow close]): + (-[DumpRenderTreeWindow startListeningForAcceleratedCompositingChanges]): + (-[DumpRenderTreeWindow webViewStartedAcceleratedCompositing:]): + +2009-12-07 Nikolas Zimmermann <nzimmermann@rim.com> + + Reviewed by Holger Hans Peter Freyther. + + Turn on (SVG) Filters support, by default. + https://bugs.webkit.org/show_bug.cgi?id=32224 + + For now only enable FILTERS build flag on WebKit/mac. Other platforms will follow soon. + + * Scripts/build-webkit: + +2009-12-07 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Darin Fisher. + + Chromium buildbots are not red when they fail + https://bugs.webkit.org/show_bug.cgi?id=32235 + + * Scripts/build-webkit: + +2009-12-07 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Darin Fisher. + + Typo in chromium linux builder + https://bugs.webkit.org/show_bug.cgi?id=32238 + + * Scripts/webkitdirs.pm: + +2009-12-07 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Darin Fisher. + + fixes to build-webkit --chromium + https://bugs.webkit.org/show_bug.cgi?id=32179 + + * Scripts/webkitdirs.pm: + +2009-12-07 Eric Seidel <eric@webkit.org> + + No review, just adding two recently approved committers. + + * Scripts/modules/committers.py: + +2009-12-07 Alexey Proskuryakov <ap@apple.com> + + * Scripts/run-webkit-websocketserver: Added property svn:executable. + +2009-12-07 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Alexey Proskuryakov. + + Add run-webkit-websocketserver + + https://bugs.webkit.org/show_bug.cgi?id=31390 + + * Scripts/run-webkit-websocketserver: Added. + +2009-12-07 Steve Falkenburg <sfalken@apple.com> + + Build fix. Be flexible about which version of ICU is used on Windows. + + * DumpRenderTree/win/DumpRenderTree.vcproj: Add optional xcopy commands to copy ICU 4.2. + +2009-12-07 Dirk Schulze <krit@webkit.org> + + Not reviewed, adding myself to the reviewers list. + + * Scripts/modules/committers.py: + +2009-12-06 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Add an API for uploading results files to StatusBot + https://bugs.webkit.org/show_bug.cgi?id=32210 + + Add Content-Type: plain/text which was forgotten + from the previous commit when I landed with land-patches + instead of land-diff. + + * QueueStatusServer/queue_status.py: + +2009-12-06 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Status Server needs a way to handle uploaded results + https://bugs.webkit.org/show_bug.cgi?id=32209 + + * QueueStatusServer/queue_status.py: Add a ShowResults (results/*) command + * QueueStatusServer/update_status.html: Add file upload. + +2009-12-06 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Add an API for uploading results files to StatusBot + https://bugs.webkit.org/show_bug.cgi?id=32210 + + * Scripts/modules/statusbot.py: + +2009-12-06 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Status Server needs a way to handle uploaded results + https://bugs.webkit.org/show_bug.cgi?id=32209 + + * QueueStatusServer/queue_status.py: Add a ShowResults (results/*) command + * QueueStatusServer/update_status.html: Add file upload. + +2009-12-06 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] BuildAttachment should not check the builders + https://bugs.webkit.org/show_bug.cgi?id=32207 + + This is code that got copied here by accident when the class was created. + + * Scripts/modules/commands/download.py: + +2009-12-06 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Make LandingSequence.update aware of ports + https://bugs.webkit.org/show_bug.cgi?id=32208 + + This is required to make build work on the Chromium port because + Chromium has a custom update-webkit. + + * Scripts/modules/landingsequence.py: + +2009-12-06 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Add early warning system commands to bugzilla-tool. + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitport.py: + +2009-12-06 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Implement Qt EarlyWarningSystem and Chromium EarlyWarningSystem + https://bugs.webkit.org/show_bug.cgi?id=32205 + + * Scripts/modules/commands/early_warning_system.py: Added. + * Scripts/modules/commands/queues.py: + +2009-12-06 Dan Bernstein <mitz@apple.com> + + Reviewed by Adele Peterson. + + REGRESSION (r51728): update-webkit fails when the current directory is + not inside a Subversion working copy + https://bugs.webkit.org/show_bug.cgi?id=32204 + + * Scripts/update-webkit: Invoke isSVN() in the correct working + directory. + +2009-12-06 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Rename AbstractTryQueue to AbstractReviewQueue + https://bugs.webkit.org/show_bug.cgi?id=32202 + + * Scripts/modules/commands/queues.py: + +2009-12-05 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Implement ChromiumPort + https://bugs.webkit.org/show_bug.cgi?id=32182 + + * Scripts/modules/webkitport.py: + * Scripts/modules/webkitport_unittest.py: + +2009-12-05 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move update-webkit into BuildSteps + https://bugs.webkit.org/show_bug.cgi?id=32181 + + We need to move update-webkit out of SCM.py because SCM isn't supposed to know + that WebKit exists. The proper place for the knowledge of the existence of + update-webkit is in WebKitPort because some ports have specialized update + scripts (analogous to build-webkit). + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/scm.py: + * Scripts/modules/webkitport.py: + +2009-12-05 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + update-webkit should call git svn rebase and resolve-ChangeLogs -f + https://bugs.webkit.org/show_bug.cgi?id=27162 + + Teach update-webkit about Git. I didn't add the call to + resolve-ChangeLogs -f because sometimes that script goes bananas. We + can iterate from here, however. + + * Scripts/update-webkit: + +2009-12-04 Yael Aharon <yael.aharon@nokia.com> + + Unreviewed build fix. + + [Qt] build fix after r51634 removed unused QBoxLayout include which included limits.h. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + +2009-12-04 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Adam Treat. + + Add some missing methods for showing and hiding the + Web Inspector. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::WebPage): + (WebCore::WebPage::~WebPage): + (WebCore::WebPage::webInspector): + * DumpRenderTree/qt/DumpRenderTree.h: + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::showWebInspector): + (LayoutTestController::hideWebInspector): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2009-12-04 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Adam Treat. + + Implement the setAllowUniversalAccessFromFileURLs method + for the Qt LayoutTestController. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::resetSettings): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2009-12-04 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Darin Adler. + + REGRESSION: AX: buttons now extremely repetitive + https://bugs.webkit.org/show_bug.cgi?id=32164 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (isAttributeSupportedCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::isAttributeSupported): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::isAttributeSupported): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::isAttributeSupported): + +2009-12-03 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by David Levin. + + check-webkit-style should check for camelCase variable names + https://bugs.webkit.org/show_bug.cgi?id=32051 + + * Scripts/modules/cpp_style.py: + * Scripts/modules/cpp_style_unittest.py: + +2009-12-03 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Eric Seidel. + + AX: VO just says "term" on many web sites + https://bugs.webkit.org/show_bug.cgi?id=32139 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (getRoleDescriptionCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::roleDescription): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::roleDescription): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::roleDescription): + +2009-12-03 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Beth Dakin. + + Implement WAI-ARIA scrollbar role and related property aria-orientation + https://bugs.webkit.org/show_bug.cgi?id=32126 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (getOrientationCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::orientation): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::orientation): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::orientation): + +2009-12-03 Eric Carlson <eric.carlson@apple.com> + + Reviewed by Adam Roben. + + Minor correction to r51663. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (addQTDirToPATH): + GetEnvironmentVariable -> GetEnvironmentVariableW. + +2009-12-03 Eric Carlson <eric.carlson@apple.com> + + Reviewed by Adam Roben. + + ~96 regression tests fail when using QuickTime 7.6 (they pass with QuickTime 7.3) + https://bugs.webkit.org/show_bug.cgi?id=30256 + + Add the QuickTime dll directory to the PATH environment variable so + inialization can succeed. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (addQTDirToPATH): + (initialize): + +2009-12-03 Shu Chang <Chang.Shu@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] After revision 32643, sender() is of type QWebPage instead of QWebFrame. + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::maybeDump): + +2009-12-03 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Eric Seidel. + + Update pywebsocket to 0.4.3 + This version logs friendlier and higher-level messages in WARN level, which is used for LayoutTests. + Stack trace is logged now in INFO level. + https://bugs.webkit.org/show_bug.cgi?id=32097 + + * pywebsocket/mod_pywebsocket/dispatch.py: + * pywebsocket/mod_pywebsocket/msgutil.py: + * pywebsocket/mod_pywebsocket/standalone.py: + * pywebsocket/mod_pywebsocket/util.py: + * pywebsocket/setup.py: + * pywebsocket/test/test_dispatch.py: + * pywebsocket/test/test_util.py: + +2009-12-03 Sam Weinig <sam@webkit.org> + + Reviewed by Gavin Barraclough. + + Fix JSClassRef leak. + + * DumpRenderTree/AccessibilityController.cpp: + (AccessibilityController::makeWindowObject): + * DumpRenderTree/GCController.cpp: + (GCController::makeWindowObject): + * DumpRenderTree/LayoutTestController.cpp: + (LayoutTestController::makeWindowObject): + +2009-12-03 Brady Eidson <beidson@apple.com> + + Reviewed by Sam Weinig. + + <rdar://problem/7214236> and http://webkit.org/b/32052 - Implement HTML5 state object history API + + Keep DRT-win building... + + * DumpRenderTree/win/FrameLoadDelegate.h: + (FrameLoadDelegate::didPushStateWithinPageForFrame): + (FrameLoadDelegate::didReplaceStateWithinPageForFrame): + (FrameLoadDelegate::didPopStateWithinPageForFrame): + +2009-12-03 Andras Becsi <abecsi@inf.u-szeged.hu> + + Unreviewed build fix. + + [Qt] ARM-Linux build fix after r51634 removed unused QBoxLayout include which included limits.h on ARM. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + +2009-12-03 Andras Becsi <abecsi@inf.u-szeged.hu> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Refactor DRT to not crash on tests which open child windows from javascript. + Prevent DRT from showing the main view if these childs get deleted. + This fixes https://bugs.webkit.org/show_bug.cgi?id=31591. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::WebPage): + (WebCore::DumpRenderTree::DumpRenderTree): + (WebCore::DumpRenderTree::~DumpRenderTree): + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + (WebCore::DumpRenderTree::open): + (WebCore::DumpRenderTree::closeRemainingWindows): + (WebCore::DumpRenderTree::createWindow): + (WebCore::DumpRenderTree::windowCount): + * DumpRenderTree/qt/DumpRenderTree.h: + +2009-12-02 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + REGRESSION(51595): commit-queue is throwing exceptions + https://bugs.webkit.org/show_bug.cgi?id=32083 + + * Scripts/modules/commands/queues.py: + - Don't use default value of [] as it ends up getting shared. + - Make log_progress accept arrays of ints as well as strings. + - Return an exit code from execute() + * Scripts/modules/commands/queues_unittest.py: Added. + - Test to make sure log_progress will accept ints. + - Test to make sure run_bugzilla_tool will accept ints. + * Scripts/modules/workqueue.py: + - Print the stack trace on unexpected exceptions for easier debugging. + * Scripts/run-webkit-unittests: + - Add queues_unittest. + + * Scripts/modules/commands/queues.py: + * Scripts/modules/commands/queues_unittest.py: Copied from WebKitTools/Scripts/modules/commands/commandtest.py. + * Scripts/modules/mock_bugzillatool.py: + * Scripts/modules/workqueue.py: + * Scripts/run-webkit-unittests: + +2009-12-02 David Levin <levin@chromium.org> + + Reviewed by Adam Barth. + + check-webkit-style is too noisy about namespace indenting issues. + https://bugs.webkit.org/show_bug.cgi?id=32096 + + * Scripts/modules/cpp_style.py: + Added a _FileState object to be able to track file level information. In this + case, it simply tracks whether the error has already been given, so that it isn't + done again. + * Scripts/modules/cpp_style_unittest.py: + Modified test cases to pass in the _FileState object and fix a test that expected + to see the namespace error twice (now it only occurs once). No new tests because + existing tests cover the change in functionality. + +2009-12-01 Kevin Ollivier <kevino@theolliviers.com> + + Reviewed by Eric Seidel. + + [wx] Get DumpRenderTree building after waf switch. + + https://bugs.webkit.org/show_bug.cgi?id=32041 + + * DumpRenderTree/wscript: Added. + * DumpRenderTree/wx/LayoutTestControllerWx.cpp: + (LayoutTestController::setTimelineProfilingEnabled): + (LayoutTestController::evaluateScriptInIsolatedWorld): + (LayoutTestController::disableImageLoading): + (LayoutTestController::whiteListAccessFromOrigin): + (LayoutTestController::counterValueForElementById): + * Scripts/build-webkit: + +2009-12-02 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] build-attachment shouldn't check whether the builders are red + https://bugs.webkit.org/show_bug.cgi?id=32062 + + build-attachment doesn't touch the remote repository, so there isn't a + need to hold off when the builders are red. + + * Scripts/modules/commands/download.py: + * Scripts/modules/landingsequence.py: + +2009-12-02 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + REGRESSION(51590): style-queue and build-queue think their empty when they are not + https://bugs.webkit.org/show_bug.cgi?id=32061 + + * Scripts/modules/bugzilla.py: make all id lookups return ints instead of strings. + * Scripts/modules/bugzilla_unittest.py: Add and update unit tests to use ints. + +2009-12-02 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + build-queue is throwing exceptions and complaining about + lack of --no-update on build-attachment. Make it stop. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + +2009-12-02 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. Forgot to declare my variables. :( + + * QueueStatusServer/queue_status.py: + +2009-12-02 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Implement status bubble view + https://bugs.webkit.org/show_bug.cgi?id=32057 + + The status bubble is a compact representation of the queue status for a + given patch. This will eventually help us reduce the comment spam from + the queues. + + * QueueStatusServer/index.html: Added HTML5 doctype for sanity. + * QueueStatusServer/queue_status.py: + * QueueStatusServer/status_bubble.html: Added. + +2009-12-02 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + trim commands/*.py includes now that commands are unit tested + https://bugs.webkit.org/show_bug.cgi?id=32056 + + In the course of ensuring that the unit tests still + passed after this change, I had to actually make them + pass in the first place. + + * Scripts/modules/bugzilla.py: + - Fix _parse_attachment_ids_request_query to return ints instead of strings. + * Scripts/modules/commands/download.py: Remove unneeded imports. + * Scripts/modules/commands/queries.py: Ditto. + * Scripts/modules/commands/queues.py: Ditto. + * Scripts/modules/commands/upload.py: Ditto. + * Scripts/modules/scm_unittest.py: + - Fix to expect the \n after the path since echo adds an \n. + +2009-12-01 Yaar Schnitman <yaar@chromium.org> + + build-webkit: Remove flex,bison,gperf check for chromium + https://bugs.webkit.org/show_bug.cgi?id=32043 + + * Scripts/webkitdirs.pm: + +2009-12-01 Chris Fleizach <cfleizach@apple.com> + + Build fixage for Windows/GTK for DumpRenderTree. + + WAI-ARIA: implement support for ARIA drag and drop + https://bugs.webkit.org/show_bug.cgi?id=32007 + + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::ariaIsGrabbed): + (AccessibilityUIElement::ariaDropEffects): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::ariaIsGrabbed): + (AccessibilityUIElement::ariaDropEffects): + +2009-12-01 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Darin Adler. + + WAI-ARIA: implement support for ARIA drag and drop + https://bugs.webkit.org/show_bug.cgi?id=32007 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (getARIADropEffectsCallback): + (getARIAIsGrabbedCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::ariaIsGrabbed): + (AccessibilityUIElement::ariaDropEffects): + +2009-12-01 Nikolas Zimmermann <nzimmermann@rim.com> + + Not reviewed. GTK DRT try 2. + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + +2009-12-01 Nikolas Zimmermann <nzimmermann@rim.com> + + Not reviewed. Try to fix gtk DRT build. + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + +2009-12-01 David Levin <levin@chromium.org> + + Reviewed by Eric Seidel. + + check-webkit-style does not understand (Prefix)Foo(Custom|Gtk|CG|Mac).cpp including Foo.h + https://bugs.webkit.org/show_bug.cgi?id=32033 + + * Scripts/modules/cpp_style.py: Changed check for a possible primary header to use find + instead of startswith. + * Scripts/modules/cpp_style_unittest.py: Added a unit test for the new behavior. + +2009-12-01 Nikolas Zimmermann <nzimmermann@rim.com> + + Not reviewed. Qt build almost fixed, DumpRenderTree should compile now as well. + + * DumpRenderTree/qt/LayoutTestControllerQt.h: Update old function signature. + +2009-12-01 Nikolas Zimmermann <nzimmermann@rim.com> + + Reviewed by Simon Fraser. + + Add SVG animation test framework with 'snapshot' functionality + https://bugs.webkit.org/show_bug.cgi?id=31897 + + Add new 'sampleSVGAnimationForElementAtTime' DRT method, + used by the new SVG animation testing framework, implemented + for qt/gtk/win/mac. + + * DumpRenderTree/LayoutTestController.cpp: + (sampleSVGAnimationForElementAtTimeCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + * DumpRenderTree/wx/LayoutTestControllerWx.cpp: + (LayoutTestController::sampleSVGAnimationForElementAtTime): + +2009-12-01 Adam Roben <aroben@apple.com> + + Remove user content before running each test on Windows + + Fixes <http://webkit.org/b/31479> Make websocket tests work on Windows + + Reviewed by Alexey Proskuryakov. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (resetWebViewToConsistentStateBeforeTesting): Remove all user content + before each test, which matches Mac. + +2009-12-01 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Alexey Proskuryakov. + + Make pywebsocket log errors to a file + https://bugs.webkit.org/show_bug.cgi?id=31604 + + * Scripts/run-webkit-tests: Log output to a pywebsocket_log.txt file in results directory + (by passing the path to the server via a recently added -l option). + +2009-12-01 Adam Roben <aroben@apple.com> + + Fix tests that use the TestNetscapePlugin in Debug_Internal builds + + Reviewed by Ada Chan. + + Fixes <http://webkit.org/b/32027> REGRESSION (r49705): Tests that use + TestNetscapePlugin fail in Debug_Internal builds + + * DumpRenderTree/win/DumpRenderTree.cpp: Use the "_Debug" plugin + directory in Debug and Debug_All builds, but not in Debug_Internal + builds. + +2009-12-01 Adam Roben <aroben@apple.com> + + Re-enable DRT's watchdog timer on Windows + + It was accidentally disabled in r50907. + + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::setWaitToDump): Remove a "false &&" that snuck + into an if condition. + +2009-12-01 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + check-webkit-style complains about #imports with / in them + https://bugs.webkit.org/show_bug.cgi?id=32022 + + We need to exclude #import directives in addition to #include from the + binary operator whitespace checks. + + * Scripts/modules/cpp_style.py: + * Scripts/modules/cpp_style_unittest.py: + +2009-11-30 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + check-webkit-style is slightly too verbose + https://bugs.webkit.org/show_bug.cgi?id=32010 + + check-webkit-style prints out a bunch of "done" lines that seem redundant, + especially for the style-queue. + + * Scripts/modules/cpp_style.py: + +2009-11-30 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Eric Seidel. + + [GTK] Moved the functions that are closing the result log of the + tests to the last line of the runTest function, this avoids + reporting crashes in the wrong test if there is a problem when + reseting the state after the test. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (dump): + (runTest): + +2009-11-30 Sam Weinig <sam@webkit.org> + + Reviewed by Geoffrey Garen. + + Fix for https://bugs.webkit.org/show_bug.cgi?id=31286 + fast/js/date-proto-generic-invocation breaks another test + + Don't cache JSClassRefs, a change to the prototype chain will + last between tests. + + * DumpRenderTree/AccessibilityController.cpp: + (AccessibilityController::getJSClass): + * DumpRenderTree/GCController.cpp: + (GCController::getJSClass): + * DumpRenderTree/GCController.h: + * DumpRenderTree/LayoutTestController.cpp: + (LayoutTestController::getJSClass): + +2009-11-30 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Alexey Proskuryakov. + + Update pywebsocket to 0.4.2.1. + This is to fix a bug that some messages are logged to stderr even when the log file is specified. + https://bugs.webkit.org/show_bug.cgi?id=31976 + + * pywebsocket/mod_pywebsocket/standalone.py: + * pywebsocket/setup.py: + +2009-11-30 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Generate pass messages for style-queue + https://bugs.webkit.org/show_bug.cgi?id=31995 + + With this change, the style-queue posts "pass" messages to bugs as + well. Also, added more information to the state store w.r.t. passing + and failing. + + * Scripts/modules/commands/queues.py: + * Scripts/modules/patchcollection.py: + +2009-11-30 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Reviewed by Simon Fraser. + + Check for WEBKIT_TESTFONTS environment variable in + run-webkit-tests, for GTK+, to have a more prominent error + message. + + * Scripts/run-webkit-tests: + +2009-11-30 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Dimitri Glazkov. + + update-webkit --chromium forces gclient sync + + https://bugs.webkit.org/show_bug.cgi?id=31967 + + * Scripts/update-webkit-chromium: + +2009-11-29 Eric Seidel <eric@webkit.org> + + Add Erik Arvidsson to committers.py since by r51326 he clear has commit rights. + + * Scripts/modules/committers.py: + +2009-11-29 Eric Seidel <eric@webkit.org> + + No review, just adding missing svn property. + + Set svn:executable property on new scripts. + These were added by commit-queue commits, but the executable + property was lost due to a bug in svn-apply: + https://bugs.webkit.org/show_bug.cgi?id=27204 + + * Scripts/update-webkit-chromium: Added property svn:executable. + * Scripts/validate-committer-lists: Added property svn:executable. + +2009-11-29 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Need a way to validate that committers.py includes all committers/reviewers + https://bugs.webkit.org/show_bug.cgi?id=30970 + + Add a script which knows how to compare our public committer/reviewer + lists and show discrepancies between them. + + Validates mailing lists vs. committers.py: + - committers.py committers missing from webkit-committers@lists + - webkit-committers@lists members missing from committers.py + - committers.py reviewers missing from webkit-reviewers@lists + - webkit-reviewers@lists members missing from committers.py + - webkit-reviewers@lists members missing from committers.py reviewer list + + Validates committers.py vs. trunk/ SVN history: + - committers who have not committed in over a year + - SVN committers missing from committers.py + - committers.py members who have no record in SVN. + + All of these lists still show "false positives" until a few more committers.py updates are made, like: + https://bugs.webkit.org/show_bug.cgi?id=31366 + + * Scripts/validate-committer-lists: Added. + +2009-11-28 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] style-queue sends ~100 requests to QueueStatusServer every 5 minutes + https://bugs.webkit.org/show_bug.cgi?id=31950 + + Now we cache the last status that we get back from QueueStatusServer. + Eventually we'll have to do something more fancy if we want to support + a "try again" button on QueueStatusServer, but we can cross that bridge + when we come to it. + + * Scripts/modules/patchcollection.py: + +2009-11-28 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + StyleQueue scans ~100 bug pages every 5 minutes + https://bugs.webkit.org/show_bug.cgi?id=31947 + + Instead of getting the pending-review attachment ids by scanning each + bug (which results in a network request), we should just get them all + from webkit.org/pending-review in one shot. + + * Scripts/modules/bugzilla.py: + * Scripts/modules/bugzilla_unittest.py: + * Scripts/modules/commands/queries.py: + * Scripts/modules/commands/queries_unittest.py: + * Scripts/modules/commands/queues.py: + * Scripts/modules/mock_bugzillatool.py: + * Scripts/modules/patchcollection.py: + +2009-11-28 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] CC webkit-bot-watchers whenever the bots touch bugs + https://bugs.webkit.org/show_bug.cgi?id=31952 + + The mailing list is open for anyone to subscribe. + + * Scripts/modules/bugzilla.py: + * Scripts/modules/commands/queues.py: + +2009-11-28 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] style-queue should report style errors to bugzilla + https://bugs.webkit.org/show_bug.cgi?id=31945 + + Currently, we're just logging the style errors locally. With this + patch we'll actually log the errors to bugzilla. Note: I plan to run + with the "local-only" logging during development. + + * Scripts/modules/commands/queues.py: + * Scripts/modules/scm.py: + +2009-11-28 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] style-queue shouldn't reject patches from the commit-queue + https://bugs.webkit.org/show_bug.cgi?id=31944 + + Currently the style-queue subprocess gets confused and thinks its the + commit-queue. If the patch has an error, it rejects it from the + commit-queue. Instead, we should have style-queue specific logic. + This patch doesn't add that logic, but it gives us a callback we can + use to add that logic. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/queues.py: + * Scripts/modules/landingsequence.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Rubber stamped by Eric Seidel. + + Rename CommitQueueStatus to QueueStatusServer to allow for more queues. + + * CommitQueueStatus: Removed. + * CommitQueueStatus/app.yaml: Removed. + * CommitQueueStatus/filters: Removed. + * CommitQueueStatus/filters/__init__.py: Removed. + * CommitQueueStatus/filters/webkit_extras.py: Removed. + * CommitQueueStatus/index.html: Removed. + * CommitQueueStatus/index.yaml: Removed. + * CommitQueueStatus/queue_status.py: Removed. + * CommitQueueStatus/stylesheets: Removed. + * CommitQueueStatus/stylesheets/main.css: Removed. + * CommitQueueStatus/update_status.html: Removed. + * QueueStatusServer: Copied from WebKitTools/CommitQueueStatus. + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + style-queue should only process each patch once + https://bugs.webkit.org/show_bug.cgi?id=31939 + + Actually address reviewer comments! + + * Scripts/bugzilla-tool: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] style-queue fails to apply a bunch of patches for no reason + https://bugs.webkit.org/show_bug.cgi?id=31942 + + By passing --non-interactive to check-style, we convince check-style + to pass --force to svn-apply, which lets it apply more patches. + + * Scripts/modules/commands/queues.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Support --status-host in style-queue + https://bugs.webkit.org/show_bug.cgi?id=31941 + + * Scripts/modules/commands/queues.py: + * Scripts/modules/statusbot.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + style-queue should only process each patch once + https://bugs.webkit.org/show_bug.cgi?id=31939 + + Before processing a patch, the try-queues now ask the web service + whether they have already processed the patch. This is an initial cut + of this functionality. I expect we're make it richer over time. + + * Scripts/bugzilla-tool: + * Scripts/modules/commands/queues.py: + * Scripts/modules/patchcollection.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + PatchStatus does not return status + https://bugs.webkit.org/show_bug.cgi?id=31938 + + We need to parse attachment_id as an int. Otherwise, we can't find + anything in the datastore. + + * CommitQueueStatus/queue_status.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Make commit-queue status not throw an exception where there is not + status + https://bugs.webkit.org/show_bug.cgi?id=31936 + + We need to actually fetch the results of the query to see what's in the + datastore. + + * CommitQueueStatus/queue_status.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] unit test ApplyPatches and ApplyAttachment + https://bugs.webkit.org/show_bug.cgi?id=31935 + + * Scripts/modules/commands/download.py: + * Scripts/modules/commands/download_unittest.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Unit test download commands + https://bugs.webkit.org/show_bug.cgi?id=31923 + + Adds download_unittest and fixes a bug found while testing. + + * Scripts/modules/commands/commandtest.py: + * Scripts/modules/commands/download.py: + Fixed a bug where we'd throw an error because [].append returns + None. + * Scripts/modules/commands/download_unittest.py: Added. + * Scripts/modules/mock_bugzillatool.py: + * Scripts/run-webkit-unittests: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix found while writing unit tests. + + * Scripts/modules/commands/download.py: + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 6: Kill the rest. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/commands/queries.py: + * Scripts/modules/commands/upload.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/scm.py: + * Scripts/modules/webkitlandingscripts.py: Removed. + +2009-11-27 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Unit test upload commands + https://bugs.webkit.org/show_bug.cgi?id=31903 + + Adds unit tests for all but two of the upload commands. The two + remaining ones are more difficult. I'll return to them later. The + goal of these tests is just to run the commands. We can test more + detailed behavior later. + + * Scripts/modules/commands/commandtest.py: + * Scripts/modules/commands/upload.py: + * Scripts/modules/commands/upload_unittest.py: + * Scripts/modules/mock.py: Added. + * Scripts/modules/mock_bugzillatool.py: + +2009-11-26 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 5: Kill run_and_throw_if_fail. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/processutils.py: Added. + * Scripts/modules/webkitlandingscripts.py: + +2009-11-26 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 4: Kill run_webkit_script. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-26 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 3: Kill build_webkit. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-26 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 2: Kill ensure_builders_are_green. + + * Scripts/modules/buildsteps.py: + * Scripts/modules/commands/download.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-26 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + [bzt] Kill WebKitLandingScripts + https://bugs.webkit.org/show_bug.cgi?id=31904 + + Step 1: Kill prepare_clean_working_directory and run_webkit_tests. + + * Scripts/bugzilla-tool: + * Scripts/modules/buildsteps.py: Added. + * Scripts/modules/commands/download.py: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-26 Zoltan Horvath <zoltan@webkit.org> + + Reviewed by Adam Barth. + + Remove directory prefixes from linux commands + + SCM unittests can cause errors on some systems if we use absolute reference to + the commands, so directory prefixes have been removed. + + * Scripts/modules/scm_unittest.py: + +2009-11-21 Holger Hans Peter Freyther <zecke@selfish.org> + + Reviewed by Kenneth Rohde Christiansen. + + Link DumpRenderTree to the Qt Ui Tools + https://bugs.webkit.org/show_bug.cgi?id=31203 + + Implement QWebPage::createPlugin using the Qt Ui Tools + to be able to create classes like QProgressBar from within + the <object></object> tags This is required for the + new automatic test of Qt Plugins. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::createPlugin): + * DumpRenderTree/qt/DumpRenderTree.h: + * DumpRenderTree/qt/DumpRenderTree.pro: + +2009-11-25 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Eric Seidel. + + Update pywebsocket to 0.4.2 + + Update pywebsocket to 0.4.2 + https://bugs.webkit.org/show_bug.cgi?id=31861 + + * pywebsocket/example/echo_client.py: + * pywebsocket/example/echo_wsh.py: + * pywebsocket/mod_pywebsocket/__init__.py: + * pywebsocket/mod_pywebsocket/dispatch.py: + * pywebsocket/mod_pywebsocket/msgutil.py: + * pywebsocket/mod_pywebsocket/standalone.py: + * pywebsocket/setup.py: + * pywebsocket/test/test_dispatch.py: + * pywebsocket/test/test_msgutil.py: + +2009-11-25 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Add unit test for mark-fixed + https://bugs.webkit.org/show_bug.cgi?id=31896 + + * Scripts/modules/commands/commandtest.py: Added. + * Scripts/modules/commands/queries_unittest.py: + * Scripts/modules/commands/upload_unittest.py: Added. + * Scripts/modules/mock_bugzillatool.py: + * Scripts/run-webkit-unittests: + +2009-11-25 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + bugzilla-tool should have a mark-fixed command + https://bugs.webkit.org/show_bug.cgi?id=31853 + + Pretty simple stuff. + + * Scripts/modules/commands/upload.py: + +2009-11-25 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + 'bugzilla-tool help' should only show common commands like how 'git help' does + https://bugs.webkit.org/show_bug.cgi?id=31772 + + I also took this opportunity to make 'help' a real Command. + Making 'help' a real command required adding Command.tool (which we've wanted to do for a while). + + * Scripts/bugzilla-tool: + - change should_show_command_help to should_show_in_main_help + * Scripts/modules/commands/download.py: + - Mark commands as being shown in main help or not. + - show_in_main_help = False is not required (default is false), + but it seemed to make the commands more self-documenting. + * Scripts/modules/commands/queries.py: ditto + * Scripts/modules/commands/queues.py: ditto + * Scripts/modules/commands/upload.py: ditto + * Scripts/modules/multicommandtool.py: + - Make Command hold a pointer to tool in self.tool. Most Command + subclasses do not take advantage of this yet, but it was required + for HelpCommand to be able to reach the tool from _help_epilog(). + - Move MultiCommandTool._standalone_help_for_command to Command.standalone_help + - Move MultiCommandTool._help_epilog to Command._help_epilog + - Move "help" logic into HelpCommand.execute() + - Change should_show_command_help to should_show_in_main_help and add a default implementation. + * Scripts/modules/multicommandtool_unittest.py: + - Test hiding of Commands in --help, and that all commands are shown in 'help --all-commands' + +2009-11-25 Brian Weinstein <bweinstein@apple.com> + + Reviewed by Dan Bernstein. + + Patch by Mark Rowe. + + The buildbots are failing on Windows because when they were upgraded + to 4.0.4, Apple Application Support was not in their path. Add it to + the path to fix the buildots. + + * Scripts/webkitdirs.pm: + +2009-11-25 Csaba Osztrogonác <ossy@webkit.org> + + Reviewed by David Kilzer. + + run-webkit-tests doesn't accept directories/files with --skipped=only parameter + https://bugs.webkit.org/show_bug.cgi?id=31799 + + * Scripts/run-webkit-tests: Fixed. + +2009-11-25 Zoltan Horvath <zoltan@webkit.org> + + Reviewed by Eric Seidel. + + Change run_command to give back stderr by default + https://bugs.webkit.org/show_bug.cgi?id=31734 + + Change run_command to give back stderr by default. + Set run_commands's 'svn-create-patch' calling to put only the stdout into the patches. + Change the related unittest call. + + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + +2009-11-25 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Centralize required argument parsing in Command + https://bugs.webkit.org/show_bug.cgi?id=31872 + + * Scripts/modules/commands/download.py: remove custom required arg message. + * Scripts/modules/commands/upload.py: ditto. + * Scripts/modules/multicommandtool.py: + - Add _parse_required_arguments. + - Pass program name off to OptionParser. + - Add name() for access to tool name. + - Add check_arguments_and_execute and make it return a return code. + - Replace a couple uses of + with %. + * Scripts/modules/multicommandtool_unittest.py: test _parse_required_arguments + +2009-11-25 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Abstract out capturing stdout/stderr into a new OutputCapture class for re-use among the various unit tests. + https://bugs.webkit.org/show_bug.cgi?id=31870 + + * Scripts/modules/commands/queries_unittest.py: Use the new class. + * Scripts/modules/multicommandtool_unittest.py: Ditto. + * Scripts/modules/outputcapture.py: Added. + +2009-11-24 Dmitry Titov <dimich@chromium.org> + + Reviewed by Eric Seidel. + + Add ENABLE_SHARED_SCRIPT feature define and flag for build-webkit + https://bugs.webkit.org/show_bug.cgi?id=31444 + + * Scripts/build-webkit: + +2009-11-24 Chris Marrin <cmarrin@apple.com> + + Reviewed by Simon Fraser. + + Changes the way 3D_RENDERING and ACCELERATED_COMPOSITING related tests are excluded + https://bugs.webkit.org/show_bug.cgi?id=27314 + + Now the script allows the directories with these tests to be included on all + platforms but Mac, where they behave the same as always. For all other platforms + the tests need to be excluded using the Skipped files, which is currently done + for all platforms (including win since we're not turned on yet) + + * Scripts/webkitdirs.pm: + +2009-11-24 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + queries_unittest.py should test command output + https://bugs.webkit.org/show_bug.cgi?id=31845 + + * Scripts/modules/commands/queries_unittest.py: + - Capture stdout and stderr and compare with expected strings. + +2009-11-24 Simon Fraser <simon.fraser@apple.com> + + No Review. + + Fix spelling error ("depenedencies"). + + * Scripts/build-webkit: + +2009-11-24 Mark Rowe <mrowe@apple.com> + + Land the configuration that includes the two debug GTK Linux builders. + + * BuildSlaveSupport/build.webkit.org-config/config.json: + +2009-11-24 David Kilzer <ddkilzer@apple.com> + + <http://webkit.org/b/31840> bisect-builds broke after r50080 + + Reviewed by Dan Bernstein. + + * Scripts/bisect-builds: + (mountAndRunNightly): Switched back to using backticks to run + the hdiutil command since exec() will terminate the existing + script, which is not what we want. Store the output of + File::Spec->devnull() in a variable for use in the hdiutil + detach commands. + +2009-11-23 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Include "config.h" to meet Coding Style Guidelines + https://bugs.webkit.org/show_bug.cgi?id=31792 + + * DumpRenderTree/pthreads/JavaScriptThreadingPthreads.cpp: + * DumpRenderTree/qt/jsobjects.cpp: + * DumpRenderTree/win/DRTDesktopNotificationPresenter.cpp: + +2009-11-23 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Reviewed by Oliver Hunt. + + Implement new required function to pass test we used to pass. This + change is required since r51294. + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + +2009-11-23 Jakub Wieczorek <faw217@gmail.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] DRT: dumpBackForwardList() does not work properly with non-file URLs. + https://bugs.webkit.org/show_bug.cgi?id=31775 + + LayoutTestController::dumpBackForwardList() should work with local URLs + as well as with normal URLs (in http tests for instance). + Currently it does not output the latter properly. + + Unskip a bunch of passing http/navigation tests. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::dumpHistoryItem): + +2009-11-22 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Oliver Hunt. + + ARIA: support aria-flowto + https://bugs.webkit.org/show_bug.cgi?id=31762 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (ariaFlowToElementAtIndexCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::ariaFlowToElementAtIndex): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::ariaFlowToElementAtIndex): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::ariaFlowToElementAtIndex): + +2009-11-22 Antonio Gomes <tonikitoo@webkit.org> + + Reviewed by Kenneth Christiansen. + + [Qt] fast/history/back-forward-reset-after-error-handling.html failing due to WorkQueue not being un-frozen + https://bugs.webkit.org/show_bug.cgi?id=31638 + + Unfreeze WorkQueue after each test execution. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-11-22 Jakub Wieczorek <faw217@gmail.com> + + Reviewed by Adam Barth. + + [Qt] DumpRenderTree should explicitly ignore any SSL certificate errors + for localhost and 127.0.0.1. + https://bugs.webkit.org/show_bug.cgi?id=31783 + + Unskip the http/tests/ssl/verify-ssl-enabled.php test, which is passing now. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::NetworkAccessManager::NetworkAccessManager): + (WebCore::NetworkAccessManager::sslErrorsEncountered): + (WebCore::WebPage::WebPage): + * DumpRenderTree/qt/DumpRenderTree.h: + +2009-11-22 Chris Evans <cevans@chromium.org> + + Reviewed by Adam Barth. + + Disable access to file:/// directory listings + https://bugs.webkit.org/show_bug.cgi?id=31329 + + Implemented setAllowUniversalAccessFromFileURLs to support testing of + file URL security. + + * DumpRenderTree/LayoutTestController.cpp: + (setAllowUniversalAccessFromFileURLsCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + * DumpRenderTree/wx/LayoutTestControllerWx.cpp: + (LayoutTestController::setAllowUniversalAccessFromFileURLs): + +2009-11-22 Jakub Wieczorek <faw217@gmail.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Fix the timeout of fast/frames/frame-navigation.html + https://bugs.webkit.org/show_bug.cgi?id=31638 + + The test is timeouting, because it uses the WorkQueue to load a document in one + of the child frames and once the loading is finished, the DRT does not dump the + tree. This is because it waits for the QWebFrame::loadFinished() signal from + the main frame, while it should connect to QWebPage::loadFinished(). + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::DumpRenderTree): + +2009-11-21 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool --help spews way too much text + https://bugs.webkit.org/show_bug.cgi?id=31771 + + * Scripts/bugzilla-tool: + - Remove self.cached_scm initialization hack. + * Scripts/modules/buildbot.py: + - Make default_host accessible to callers. + * Scripts/modules/commands/download.py: + - Phrase help for all commands consistently and remove spurious help text punctuation. + * Scripts/modules/commands/queries.py: Ditto. + * Scripts/modules/commands/queues.py: Ditto. + * Scripts/modules/commands/upload.py: Ditto. + * Scripts/modules/multicommandtool.py: + - Add HelpPrintingOptionParser.format_epilog to replace + NonWrappingEpilogIndentedHelpFormatter and allow us to lazily initialize + per-command help (thus removing the need for the cached_scm hack in BugzillaTool). + - Make --help only show a list of commands like "svn help" and "git help" do -- + previously --help was listing all commands and options. + - Sort list of commands alphabetically. + +2009-11-21 Eric Seidel <eric@webkit.org> + + No review. Fixing a typo from the previous patch for bug 31767. + + AbstractQueue.run_bugzilla_tool throws an exception + https://bugs.webkit.org/show_bug.cgi?id=31769 + + * Scripts/modules/commands/queues.py: + +2009-11-21 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + commit-queue fails to run with "permissions error" due to bad bugzilla-tool path + https://bugs.webkit.org/show_bug.cgi?id=31767 + + What we really want to test is BugzillaTool.path() instead of TrivialTool.path(). + Since we don't have a good way to test BugzillaTool pieces, I've + left out a test for now. + + * Scripts/bugzilla-tool: + - Add a path() implementation to expose bugzilla-tool's __file__ path to commit-queue. + * Scripts/modules/commands/queues.py: + - Use tool.path() instead of __file__. + * Scripts/modules/multicommandtool.py: + - Add a new path() method to MultiComandTool. + * Scripts/modules/multicommandtool_unittest.py: + - Provide a path() method. Little point in testing this mock implementation. + +2009-11-21 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. Turns out I was testing the wrong copy of + WebKitTools. + + * Scripts/modules/commands/download.py: + +2009-11-21 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Convert check-style to use LandingSequence + https://bugs.webkit.org/show_bug.cgi?id=31763 + + Instead of manipulating the working copy by hand, we should use the + LandingSequence in CheckStyle. This will make this code eaiser to + test. + + * Scripts/modules/commands/download.py: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Unit test query commands + https://bugs.webkit.org/show_bug.cgi?id=31755 + + These tests are pretty rough, but hopefully they'll grow. + + * Scripts/modules/commands/queries_unittest.py: Added. + * Scripts/modules/mock_bugzillatool.py: Added. + * Scripts/run-webkit-unittests: + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Fix a bunch of unit test regressions from our recent bugzilla-toll hacking + https://bugs.webkit.org/show_bug.cgi?id=31758 + + * Scripts/modules/multicommandtool.py: + - Allow passing of explicit commands to MultiCommandTool.__init__ + * Scripts/modules/multicommandtool_unittest.py: + - Use new Command.name naming system. + - Test Command auto-discovery. + * Scripts/modules/workqueue.py: + - bug_id no longer exists, use patch['bug_id'] instead. + * Scripts/modules/workqueue_unittest.py: + - WorkQueues require names now. + - should_proceed_with_work_item must return a patch object. + +2009-11-20 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Beth Dakin. + + WAI-ARIA: add support for aria-owns + https://bugs.webkit.org/show_bug.cgi?id=31702 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (ariaOwnsElementAtIndexCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::ariaOwnsElementAtIndex): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::ariaOwnsElementAtIndex): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::ariaOwnsElementAtIndex): + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Fix exception thrown when running the commit-queue. + + * Scripts/modules/statusbot.py: patch is optional. + * Scripts/modules/workqueue.py: WorkQUeue requires a name. + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Teach the StatusBot how to support more than just the commit-queue + https://bugs.webkit.org/show_bug.cgi?id=31754 + + * Scripts/modules/workqueue.py: Another typo. + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Teach the StatusBot how to support more than just the commit-queue + https://bugs.webkit.org/show_bug.cgi?id=31754 + + * Scripts/modules/commands/queues.py: Fix silly typo. + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Teach the StatusBot how to support more than just the commit-queue + https://bugs.webkit.org/show_bug.cgi?id=31754 + + * CommitQueueStatus/index.yaml: + - Add indices required for the new queries. + * CommitQueueStatus/queue_status.py: + - Add a patch-status page and move update_status to update-status. + - Only display "commit-queue" status records for the commit-queue. + - Add support for a queue_name property on status records. + - Fix _int_from_request to actually work. + * CommitQueueStatus/update_status.html: + - Add support for a queue_name on status records. + - Remove unused list of bug ids. + * Scripts/modules/commands/queues.py + - Make the queues pass the patch instead of the bug_id to StatusBot. + * Scripts/modules/statusbot.py: + - Support passing the queue_name to the status updates. + - Support fetching patch status with patch_status(). + * Scripts/modules/workqueue.py: + - Pass the patch to the StatusBot instead of the bug_id. + - Let WorkQueues have a name. + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move bugzilla-tool commands into their own file + https://bugs.webkit.org/show_bug.cgi?id=31752 + + This will let us write unit tests. + + * Scripts/bugzilla-tool: + * Scripts/modules/commands/__init__.py: Added. + * Scripts/modules/commands/download.py: Added. + * Scripts/modules/commands/queries.py: Added. + * Scripts/modules/commands/queues.py: Added. + * Scripts/modules/commands/upload.py: Added. + * Scripts/modules/grammar.py: Added. + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + BuildQueue should check if the tree is currently buildable + https://bugs.webkit.org/show_bug.cgi?id=31744 + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move prepare_clean_working_directory into the LandingSequence + https://bugs.webkit.org/show_bug.cgi?id=31743 + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: + +2009-11-20 Yael Aharon <yael.aharon@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + NPN_ReloadPlugins does not reload the page even if reloadPages is true. + https://bugs.webkit.org/show_bug.cgi?id=30460 + + Added code for calling NPN_ReloadPlugins with reloadPages true and false. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (pluginInvoke): + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Pass the port information to the child process + https://bugs.webkit.org/show_bug.cgi?id=31736 + + We need to do this so the child process knows what to build! + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitport.py: + * Scripts/modules/webkitport_unittest.py: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Support Qt port in build-queue + https://bugs.webkit.org/show_bug.cgi?id=31733 + + * Scripts/bugzilla-tool: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Implement a build-queue + https://bugs.webkit.org/show_bug.cgi?id=31725 + + Currently this just builds the first 10 patches in the review queue. + We'll want to do something smarter soon. + + * Scripts/bugzilla-tool: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Make commit-queue and style-queue show up in help + https://bugs.webkit.org/show_bug.cgi?id=31724 + + We need to store their names on their class to make these commands + properly register themselves with MultiCommandTool. + + * Scripts/bugzilla-tool: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Implement bugzilla-tool build-attachment + https://bugs.webkit.org/show_bug.cgi?id=31722 + + This command builds an attachment from bugzilla. It leaves the built + patch in the working copy. + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: + * Scripts/modules/webkitlandingscripts.py: + +2009-11-20 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Xan Lopez. + + [GTK] DRT release event does not create the state correctly + https://bugs.webkit.org/show_bug.cgi?id=31717 + + * WebKitTools/DumpRenderTree/gtk/EventSender.cpp: + +2009-11-20 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + MultiCommandTool should find Command objects automatically instead of with a manual list + https://bugs.webkit.org/show_bug.cgi?id=31710 + + * Scripts/bugzilla-tool: + * Scripts/modules/multicommandtool.py: + - Use some wild python-fu to crawl all the known subclasses of Command. + +2009-11-20 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. Added missing import. + + * Scripts/bugzilla-tool: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Unreviewed "build" fix. I failed to update LandingSequence.test + properly. + + * Scripts/modules/landingsequence.py: + +2009-11-20 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Create LandingSequence as the all-sing, all-dance landing class + https://bugs.webkit.org/show_bug.cgi?id=31709 + + Client can inherit from this class to carefully control exactly which + steps they wish to have happen in the landing sequence. + + * Scripts/bugzilla-tool: + * Scripts/modules/landingsequence.py: Added. + * Scripts/modules/webkitlandingscripts.py: Added. + +2009-11-19 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Abstract AbstractPatchProcessingCommand from AbstractPatchLandingCommand + https://bugs.webkit.org/show_bug.cgi?id=31707 + + This is to help when we implement build-attachment. + + * Scripts/bugzilla-tool: + +2009-11-19 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Support Qt port in bugzilla-tool + https://bugs.webkit.org/show_bug.cgi?id=31701 + + Now we support building with Qt! + + * Scripts/bugzilla-tool: + * Scripts/modules/webkitport.py: Added. + * Scripts/modules/webkitport_unittest.py: Added. + * Scripts/run-webkit-unittests: + +2009-11-19 Zoltan Horvath <zoltan@webkit.org> + + Reviewed by Adam Barth. + + Remove inserting stderr into patch in bugzilla-tool + https://bugs.webkit.org/show_bug.cgi?id=29914 + + Modify SCM python module's run_command function to avoid return of stderr + by default, so stderr won't be inserted into the patches. + Modify the related unit test. + + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool needs per-command help + https://bugs.webkit.org/show_bug.cgi?id=31697 + + Added support for "bugzilla-tool help command-name" + and a unit test to make sure it works. + + * Scripts/modules/multicommandtool.py: + * Scripts/modules/multicommandtool_unittest.py: + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Move MultiCommandTool and Command into a separate file and add some basic unit tests + https://bugs.webkit.org/show_bug.cgi?id=31695 + + * Scripts/bugzilla-tool: + * Scripts/modules/multicommandtool.py: Added. + * Scripts/modules/multicommandtool_unittest.py: Added. + * Scripts/run-webkit-unittests: + +2009-11-19 Eric Seidel <eric@webkit.org> + + No review, just adding a FIXME. + + Split out command parsing and help printing from BugzillaTool + https://bugs.webkit.org/show_bug.cgi?id=31688 + + * Scripts/bugzilla-tool: Add an extra comment about current design failures. + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Split out command parsing and help printing from BugzillaTool + https://bugs.webkit.org/show_bug.cgi?id=31688 + + * Scripts/bugzilla-tool: + - Add new MultiCommandTool class to contain option parsing and help printing logic. + - Rename private methods to use _ pattern. + - MultiCommandTool has two abstract methods should_show_command_help and should_execute_command. + - + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Re-factor help printing to use modern python idioms + https://bugs.webkit.org/show_bug.cgi?id=31685 + + * Scripts/bugzilla-tool: + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Darin Adler. + + commit-queue empty queue logs twice + https://bugs.webkit.org/show_bug.cgi?id=31679 + + * Scripts/bugzilla-tool: + +2009-11-19 Eric Seidel <eric@webkit.org> + + Reviewed by Darin Adler. + + bugzilla-tool's reviewer/committer rejection message should be clearer + https://bugs.webkit.org/show_bug.cgi?id=31126 + + Add more explanatory prose to bugzilla-tool's flag permission rejection message. + + * Scripts/modules/bugzilla.py: + +2009-11-19 Eric Z. Ayers <zundel@google.com> + + Reviewed by Pavel Feldman. + + Forces a WM_PAINT event on calling layoutTestController.display() + in order to enable the timeline-paint.html test on Windows. + ::UpdateWindow() does not force an event becaue the window is + not visible. + + https://bugs.webkit.org/show_bug.cgi?id=31402 + + * DumpRenderTree/win/DumpRenderTree.cpp: + (displayWebView): + +2009-11-19 Michelangelo De Simone <michelangelo@webkit.org> + + No review needed. + + Added myself to committers list. + + * Scripts/modules/committers.py: + +2009-11-19 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Bugzilla-tool command classes should match command names + https://bugs.webkit.org/show_bug.cgi?id=31666 + + I renamed all the commands except CommitMessageForCurrentDiff because + the new name would conflict with an existing class. + + * Scripts/bugzilla-tool: + +2009-11-19 Pavel Feldman <pfeldman@chromium.org> + + Reviewed by Gustavo Noronha Silva. + + Web Inspector: Implement "show inspector" in WebKit GTK + API and enable console tests. + + https://bugs.webkit.org/show_bug.cgi?id=31669 + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::showWebInspector): + +2009-11-19 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Minor refactoring + more documentation. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::clearHistory): + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-11-19 Fumitoshi Ukai <ukai@chromium.org> + + Reviewed by Eric Seidel. + + Ignore websocket tests when --no-http is specified. + https://bugs.webkit.org/show_bug.cgi?id=31662 + + * Scripts/run-webkit-tests: + +2009-11-18 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Normalize ' and " in bugzilla-tool + https://bugs.webkit.org/show_bug.cgi?id=31655 + + We decided " is better than ' and we should be consistent. + + * Scripts/bugzilla-tool: + +2009-11-18 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Remove support for Qt v4.3 or older versions + https://bugs.webkit.org/show_bug.cgi?id=29469 + + * DumpRenderTree/qt/DumpRenderTree.pro: + * DumpRenderTree/qt/ImageDiff.pro: + +2009-11-18 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Eric Seidel. + + GitTest.test_create_binary_patch fails if /tmp is symlink + https://bugs.webkit.org/show_bug.cgi?id=31536 + + * Scripts/modules/scm_unittest.py: + +2009-11-18 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Darin Adler. + + Configuration for Chromium Build Slaves. + + https://bugs.webkit.org/show_bug.cgi?id=31442 + + * BuildSlaveSupport/build.webkit.org-config/config.json: + * BuildSlaveSupport/build.webkit.org-config/master.cfg: + +2009-11-18 Eric Seidel <eric@webkit.org> + + Reviewed by Darin Adler. + + workqueue.py results in totally broken commit-queue UI + https://bugs.webkit.org/show_bug.cgi?id=31645 + + * Scripts/bugzilla-tool: + - Remove unneeded use of PatchCollection. + - Grab a new copy of the cq'd patches every run of the queue. + +2009-11-18 Sam Weinig <sam@webkit.org> + + Reviewed by Anders Carlsson. + + Make the Mac Geolocation API async. + + Update DRT to use the new async Mac Geolocation API. + + * DumpRenderTree/mac/UIDelegate.mm: + (-[UIDelegate webView:decidePolicyForGeolocationRequestFromOrigin:frame:listener:]): + +2009-11-18 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Add and option to toggle HTML5 datalist support to build-webkit + https://bugs.webkit.org/show_bug.cgi?id=31599 + + * Scripts/build-webkit: + +2009-11-18 Kevin Watters <kevinwatters@gmail.com> + + Reviewed by Kevin Ollivier. + + Enable wx plugin support using the Windows implementation as a base. + + https://bugs.webkit.org/show_bug.cgi?id=31636 + + * wx/build/settings.py: + +2009-11-18 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Change the initialization order so that the controllers + will be created before exporting them to the JS DOM window. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::DumpRenderTree): + +2009-11-18 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Fix a code copy and paste error. m_page should be page. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::createWindow): + +2009-11-17 Mark Rowe <mrowe@apple.com> + + Reviewed by Alexey Proskuryakov. + + <http://webkit.org/b/31603> WebSocket server is confused if WebKit tests run from within /tmp on Mac OS X + + * pywebsocket/mod_pywebsocket/dispatch.py: Use os.path.realpath as it returns the canonical path of a file. + This prevents symlinks from confusing the descendant check. + +2009-11-17 Mark Rowe <mrowe@apple.com> + + Reviewed by Alexey Proskuryakov. + + <http://webkit.org/b/31602> Failing to start the WebSocket server shouldn’t terminate entire test run + + If the WebSocket server fails to start have DRT load an error page in place of tests that require the + server to be up rather than having run-webkit-tests abort immediately. + + * Scripts/run-webkit-tests: + +2009-11-17 Eric Seidel <eric@webkit.org> + + Reviewed by Darin Adler. + + commit-queue is failing to set reviewer in ChangeLogs + https://bugs.webkit.org/show_bug.cgi?id=31592 + + * Scripts/bugzilla-tool: Clarify the "applying" log message. + * Scripts/modules/bugzilla.py: + - Add a new _validate_committer_and_reviewer function as a + temporary solution until we can make a real Attachment object + which knows how to fill in its committer/reviewer fields automatically. + +2009-11-17 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Unreviewed buildbot fix. + + Revert part of earlier patch and add comment, as it + was causing timeouts on the buildbot. + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::notifyDone): + +2009-11-17 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Oliver Hunt. + + Make the timeout 15 sec as for the other DRT's and make + it print out the same output when a test timeout. + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::waitUntilDone): + (LayoutTestController::notifyDone): + (LayoutTestController::timerEvent): + +2009-11-17 Joseph Pecoraro <joepeck@webkit.org> + + Reviewed by Timothy Hatcher. + + Fixed typos in comments. + + * Scripts/modules/committers.py: + +2009-11-17 Pavel Feldman <pfeldman@chromium.org> + + Reviewed by Timothy Hatcher. + + Web Inspector: Make DRT show web inspector for tests in inspector/ folder. + - Updated DRT to show/close inspector for all tests under /inspector + - Introduced LayoutTestController::setTimelineProfilingEnabled and + WebInspector::setTimelineProfilingEnabled beside setJavaScriptProfilingEnabled + - Removed reload on each inspector test + - Renamed fast/inspector to fast/inspector-support in order not to trigger + inspector for those. + - Reimplemented timeline tests in order to get rid of reload there. + - Moved tests that don't require harness into the fast group. + + https://bugs.webkit.org/show_bug.cgi?id=31472 + + * DumpRenderTree/LayoutTestController.cpp: + (setTimelineProfilingEnabledCallback): + (closeWebInspectorCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (shouldOpenWebInspector): + (runTest): + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::setTimelineProfilingEnabled): + * DumpRenderTree/mac/DumpRenderTree.mm: + (shouldOpenWebInspector): + (runTest): + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::setTimelineProfilingEnabled): + * DumpRenderTree/win/DumpRenderTree.cpp: + (shouldOpenWebInspector): + (runTest): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::setTimelineProfilingEnabled): + +2009-11-17 Oliver Hunt <oliver@apple.com> + + Reviewed by Maciej Stachowiak. + + Incorrect use of JavaScriptCore API in DumpRenderTree + https://bugs.webkit.org/show_bug.cgi?id=31577 + + Return undefined rather than a literal null. + + * DumpRenderTree/AccessibilityUIElement.cpp: + (setSelectedTextRangeCallback): + (incrementCallback): + (decrementCallback): + (showMenuCallback): + +2009-11-16 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Beth Dakin. + + AX: aria-labelledby duplicates some of its WAI-ARIA label + https://bugs.webkit.org/show_bug.cgi?id=31565 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (titleUIElementCallback): + (getIsValidCallback): + (AccessibilityUIElement::getJSClass): + +2009-11-16 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + r50942 broke output from created windows. Make the + m_enableTextOutput a member of the DRT and not the + WebPage. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::WebPage): + (WebCore::WebPage::javaScriptAlert): + (WebCore::WebPage::javaScriptConsoleMessage): + (WebCore::WebPage::javaScriptConfirm): + (WebCore::WebPage::javaScriptPrompt): + (WebCore::WebPage::acceptNavigationRequest): + (WebCore::DumpRenderTree::DumpRenderTree): + (WebCore::DumpRenderTree::open): + (WebCore::DumpRenderTree::createWindow): + * DumpRenderTree/qt/DumpRenderTree.h: + (WebCore::DumpRenderTree::setTextOutputEnabled): + (WebCore::DumpRenderTree::isTextOutputEnabled): + (WebCore::WebPage::shouldInterruptJavaScript): + (WebCore::WebPage::isTextOutputEnabled): + (WebCore::WebPage::setViewGeometry): + +2009-11-16 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Gustavo Noronha Silva. + + Moved DumpRenderTree/gtk/TestNetscapePlugin to DumpRenderTree/unix/TestNetscapePlugin + as the implementation is being used by at least Qt and Gtk+. + + Update buildsystems as well. + + * DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro: + * DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h: + * DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h: + * DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h: + * DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp: + * GNUmakefile.am: + +2009-11-16 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Alexey Proskuryakov. + + Disable wss until all platforms support pyOpenSSL + + https://bugs.webkit.org/show_bug.cgi?id=31479 + + * Scripts/run-webkit-tests: + +2009-11-14 Antonio Gomes <tonikitoo@webkit.org> + + Reviewed by Kenneth Christiansen. + + [Qt] Implement load error pages support for Qt's DRT. + https://bugs.webkit.org/show_bug.cgi?id=31509 + + For now, it will not be a default feature, and layout tests + that want to make use of this have to explicitily call + 'handleErrorPages();' for the test source. + + Any of the other DumpRenderTree's (mac, win and gtk) + support handling error pages. Qt's will be the first. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::supportsExtension): + (WebCore::WebPage::extension): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::reset): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + (LayoutTestController::shouldHandleErrorPages): + (LayoutTestController::handleErrorPages): + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Rename the --commit-queue flag on land-* now that the commit-queue needs no special treatment + https://bugs.webkit.org/show_bug.cgi?id=31549 + + Renamed --commit-queue to --non-interactive in most places + and remove the code in land-patches which is no longer needed. + + * Scripts/bugzilla-tool: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + The commit-queue should use land-attachment + https://bugs.webkit.org/show_bug.cgi?id=31548 + + * Scripts/bugzilla-tool: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Convert CommitQueue over to PatchCollection + https://bugs.webkit.org/show_bug.cgi?id=31547 + + Also fixes a bug in workqueue and adds a test! + + * Scripts/bugzilla-tool: + * Scripts/modules/workqueue.py: + * Scripts/modules/workqueue_unittest.py: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move StyleQueue over to using PatchCollection + https://bugs.webkit.org/show_bug.cgi?id=31544 + + That's what the class it's for. + + * Scripts/bugzilla-tool: + * Scripts/modules/patchcollection.py: + * Scripts/modules/patchcollection_unittest.py: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool needs a land-attachment command + https://bugs.webkit.org/show_bug.cgi?id=31546 + + * Scripts/bugzilla-tool: + - Move all the logic into AbstractLandingCommand and + add a new LandAttachment command subclass. + - Split out _collect_patches_by_bug logging from _fetch_list_of_patches_to_land. + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Move more patch-landing code into WebKitLandingScripts in preparation for land-attachment + https://bugs.webkit.org/show_bug.cgi?id=31543 + + Just moving code and updating the one caller to use WebKitLandingScripts instead of 'self'. + + * Scripts/bugzilla-tool: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Disable the style queue from posting to the commit queue status page. + + * Scripts/bugzilla-tool: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Implement PatchCollection + https://bugs.webkit.org/show_bug.cgi?id=31541 + + This class holds a set of patches and lets clients iterate through + them. Optionally, clients can install a filter. + + * Scripts/modules/patchcollection.py: Added. + * Scripts/modules/patchcollection_unittest.py: Added. + * Scripts/run-webkit-unittests: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool land-patches will close bugs with patches r=? + https://bugs.webkit.org/show_bug.cgi?id=28230 + + The commit-queue shouldn't close patches with outstanding reviews on them, + even if many reviewers seem to be against multi-patch bugs. + + * Scripts/bugzilla-tool: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool check-style should work with attachment ids instead of bug ids + https://bugs.webkit.org/show_bug.cgi?id=31540 + + * Scripts/bugzilla-tool: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + WorkQueue is the only place that should know about special exit codes + https://bugs.webkit.org/show_bug.cgi?id=31534 + + Move LandPatchesFromBugs.handled_error to WorkQueue.exit_after_handled_error + and add tests for handling exit codes. + I also cleaned up workqueue_unittest.py more. + + * Scripts/bugzilla-tool: + * Scripts/modules/workqueue.py: + * Scripts/modules/workqueue_unittest.py: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Re-factor workqueue_unittest to allow for more than one test. + https://bugs.webkit.org/show_bug.cgi?id=31535 + + * Scripts/modules/workqueue_unittest.py: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + land-patches needs to be re-factored into smaller chunks + https://bugs.webkit.org/show_bug.cgi?id=31532 + + * Scripts/bugzilla-tool: fix a couple obvious typos. + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + land-patches needs to be re-factored into smaller chunks + https://bugs.webkit.org/show_bug.cgi?id=31532 + + The next patch will move these methods into WebKitLandingScripts. + + * Scripts/bugzilla-tool: + - Split out _land_patch and _close_bug_if_no_active_patches. + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Fix silly copy-and-paste code. I am a terrible coder. + + * Scripts/modules/bugzilla.py: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Implement a StyleQueue + https://bugs.webkit.org/show_bug.cgi?id=31537 + + The first iteration of the style queue only produces output locally. + There is also a limit of 10 patches because it's not that useful to + iterate through the entire review queue at this point. We can remove + the limit later. + + * Scripts/bugzilla-tool: + * Scripts/modules/bugzilla.py: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Unit test WorkQueue + https://bugs.webkit.org/show_bug.cgi?id=31531 + + Adds basic unit testing for WorkQueue. Just runs through one cycle. + + * Scripts/bugzilla-tool: + * Scripts/modules/statusbot.py: + * Scripts/modules/workqueue.py: + * Scripts/modules/workqueue_unittest.py: Added. + * Scripts/run-webkit-unittests: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool land-diff should know how to parse bug ids out of ChangeLogs + https://bugs.webkit.org/show_bug.cgi?id=31530 + + * Scripts/bugzilla-tool: + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool needs apply-attachment + https://bugs.webkit.org/show_bug.cgi?id=31528 + + * Scripts/bugzilla-tool: + - Add ApplyAttachment command. + - Abstract applying code into WebKitApplyingScripts. + - Rename setup_for_landing to prepare_clean_working_directory and make local_commit checking optional. + * Scripts/modules/bugzilla.py: + - Add fetch_attachment and bug_id_for_attachment_id. + * Scripts/modules/bugzilla_unittest.py: + - Add test for new parsing. + - Fix previous parsing test which broke with Adam's check-style patch (bug 31515). + +2009-11-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Document check-style's use of force_clean. + + * Scripts/bugzilla-tool: + * Scripts/modules/scm.py: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move WorkQueue to its own file + https://bugs.webkit.org/show_bug.cgi?id=31529 + + WorkQueue and WorkQueueDelegate are separate concerns from + bugzilla-tool. Also added a missing include to logging.py. + + * Scripts/bugzilla-tool: + * Scripts/modules/logging.py: + * Scripts/modules/workqueue.py: + +2009-11-16 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Move OutputTee to logging.py. + + * Scripts/bugzilla-tool: + * Scripts/modules/logging.py: + +2009-11-15 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Refactor bugzilla-tool to allow for multiple queues + https://bugs.webkit.org/show_bug.cgi?id=31513 + + Divide the commit queue class into three class to make creating + additional queues easier. + + * Scripts/bugzilla-tool: + +2009-11-15 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Eric Seidel. + + svn-apply may not handle git patches created by bugzilla-tool + https://bugs.webkit.org/show_bug.cgi?id=31457 + + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + +2009-11-15 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Eric Seidel. + + bugzilla-tool should post git binary diff + https://bugs.webkit.org/show_bug.cgi?id=31458 + + Add --binary option to Git.create_patch. + + * Scripts/modules/scm.py: + * Scripts/modules/scm_unittest.py: + +2009-11-15 Adam Barth <abarth@webkit.org> + + Reviewed by Darin Adler. + + Add bugzilla-tool check-style + https://bugs.webkit.org/show_bug.cgi?id=31515 + + * Scripts/bugzilla-tool: + * Scripts/modules/bugzilla.py: + +2009-11-13 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Make the Qt Linux only --valgrind feature, suppress errors + defined in the SuppressedValgrindErrors file. + + * Scripts/run-webkit-tests: + +2009-11-13 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Set the locale to C and not to the current one in use on the system. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-11-13 Adam Roben <aroben@apple.com> + + Add support for + http/tests/security/isolatedWorld/didClearWindowObject.html + + Fixes <http://webkit.org/b/31124> Tell the WebFrameLoadDelegate when + window objects in isolated worlds are cleared + + Reviewed by Dave Hyatt. + + * DumpRenderTree/mac/DumpRenderTreeMac.h: Declared worldIDForWorld. + + * DumpRenderTree/mac/FrameLoadDelegate.mm: + (-[FrameLoadDelegate didClearWindowObjectInStandardWorldForFrame:]): + Added. Moved code here from -webView:didClearWindowObject:forFrame:. + (-[FrameLoadDelegate didClearWindowObjectForFrame:inIsolatedWorld:]): + Added. Sets a __worldID property on the global object whose value is + the ID of this world. + (-[FrameLoadDelegate webView:didClearWindowObjectForFrame:inScriptWorld:]): + Respond to this new delegate callback by calling through to one of the + above new methods. + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (worldMap): Added. Returns a HashMap containing all the worlds we've + created. + (worldIDForWorld): Added. Returns the ID for this world, or 0 if we + haven't kept track of this world. + (LayoutTestController::evaluateScriptInIsolatedWorld): Use worldMap() + instead of declaring our own. + + * DumpRenderTree/win/DumpRenderTreeWin.h: Declared worldIDForWorld. + + * DumpRenderTree/win/FrameLoadDelegate.cpp: + (FrameLoadDelegate::didClearWindowObject): Moved code from hear to + didClearWindowObjectForFrameInStandardWorld. + (FrameLoadDelegate::didClearWindowObjectForFrameInScriptWorld): + Respond to this new delegate callback by calling through to one of the + below new methods. + (FrameLoadDelegate::didClearWindowObjectForFrameInIsolatedWorld): + Added. Sets a __worldID property on the gobal object whose value is + the ID of this world. + (FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld): + Added. Moved code here from didClearWindowObject. + + * DumpRenderTree/win/FrameLoadDelegate.h: Added the + didClearWindowObjectForFrame* functions. + + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (worldMap): Added. Returns a HashMap containing all the worlds we've + created. + (worldIDForWorld): Added. Returns the ID of this world, or 0 if we + haven't kept track of this world. + (LayoutTestController::evaluateScriptInIsolatedWorld): Use worldMap() + instead of declaring our own. + +2009-11-13 Adam Roben <aroben@apple.com> + + Finish replacing worldIDs with world objects + + The only remaining use of worldIDs was in a method only used by DRT + for the isolated worlds tests. + + Fixes <http://webkit.org/b/31414> Replace worldIDs with world objects + + Reviewed by Mark Rowe. + + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::evaluateScriptInIsolatedWorld): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::evaluateScriptInIsolatedWorld): + Updated for changes to WebFrame. Now holds the map of worldID -> world + at this level instead of making WebKit do it. + +2009-11-13 Tor Arne Vestbø <tor.arne.vestbo@nokia.com> + + Reviewed by Simon Hausmann. + + [Qt] Ensure the DRT does not output anything until first test is run + + * DumpRenderTree/qt/DumpRenderTree.cpp: + * DumpRenderTree/qt/DumpRenderTree.h: + +2009-11-13 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Tor Arne Vestbø. + + Clear the undo stack in before each new test run. + + Locally fixes 3 incorrect layouts, 9 timeouts and 7 crashes. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-11-13 Andras Becsi <becsi.andras@stud.u-szeged.hu> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Add WebKitUsesPageCachePreferenceKey overriding support to DRT's LayoutTestController. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::WebPage::resetSettings): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::overridePreference): + +2009-11-13 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Update committers.py based on svn records + https://bugs.webkit.org/show_bug.cgi?id=31366 + + This list was generated using validate-committer-lists from + https://bugs.webkit.org/show_bug.cgi?id=30970 + and makes committers.py current for committers who have + committed in the last 3 years. + + * Scripts/modules/committers.py: + +2009-11-12 Anantanarayanan G Iyengar <ananta@chromium.org> + + Reviewed by Adam Barth. + + The document-open.html test was flaky at times. The test invokes the layout test plugin + which in its destroy stream handler opens a new document. This basically tears down the + stream and the associated plugin instance. The pluginLog function in the layout test + plugin attempts to retrieve the window script object on a torn down plugin instance + which crashed consistently on windows in the debugger. The functions which issue + these logs already have a valid window script object. We now have variants of the pluginLog + function which take in a window script object with and without variable arguments. + + https://bugs.webkit.org/show_bug.cgi?id=31067 + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (pluginLogWithWindowObject): + (pluginLogWithWindowObjectVariableArgs): + (pluginLog): + (notifyTestCompletion): + (testDocumentOpen): + (testWindowOpen): + +2009-11-12 Joanmarie Diggs <joanmarie.diggs@gmail.com> + + Reviewed by Gustavo Noronha Silva. + + https://bugs.webkit.org/show_bug.cgi?id=30997 + [Gtk] Implemment AtkDocument + + Added testing support. + + * DumpRenderTree/AccessibilityUIElement.cpp: + * DumpRenderTree/AccessibilityUIElement.h: + (getDocumentEncodingCallback): + (getDocumentURICallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::documentEncoding): + (AccessibilityUIElement::documentURI): + +2009-11-12 Adam Roben <aroben@apple.com> + + Replace worldIDs with world objects + + Part of <http://webkit.org/b/31414> Implement new SPI for dealing with + user scripts/stylesheets and isolated worlds + + Reviewed by Sam Weinig. + + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::addUserScript): + (LayoutTestController::addUserStyleSheet): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::addUserScript): + (LayoutTestController::addUserStyleSheet): + Changed these functions to create a new WebJSWorld each time they're + called and to pass that world to WebKit. + +2009-11-11 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Oliver Hunt. + + need to implement aria tree roles + https://bugs.webkit.org/show_bug.cgi?id=31284 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (disclosedRowAtIndexCallback): + (selectedRowAtIndexCallback): + (isEqualCallback): + (isAttributeSettableCallback): + (isActionSupportedCallback): + (disclosedByRowCallback): + (hierarchicalLevelCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + (AccessibilityUIElement::isEqual): + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::hierarchicalLevel): + (AccessibilityUIElement::disclosedRowAtIndex): + (AccessibilityUIElement::selectedRowAtIndex): + (AccessibilityUIElement::disclosedByRow): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::getChildAtIndex): + (AccessibilityUIElement::disclosedRowAtIndex): + (AccessibilityUIElement::selectedRowAtIndex): + (AccessibilityUIElement::titleUIElement): + (AccessibilityUIElement::parentElement): + (AccessibilityUIElement::disclosedByRow): + (AccessibilityUIElement::hierarchicalLevel): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::hierarchicalLevel): + (AccessibilityUIElement::disclosedRowAtIndex): + (AccessibilityUIElement::selectedRowAtIndex): + (AccessibilityUIElement::disclosedByRow): + +2009-11-11 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Darin Adler. + + svn-apply can not handle git binary diffs + https://bugs.webkit.org/show_bug.cgi?id=26830 + + Support "literal" type git binary diffs. + + * Scripts/VCSUtils.pm: + * Scripts/modules/scm_unittest.py: + * Scripts/svn-apply: + +2009-11-11 Dmitry Titov <dimich@chromium.org> + + Not reviewed, removing duplicate entry for myself in committers.py. + + * Scripts/modules/committers.py: + +2009-11-11 Alexey Proskuryakov <ap@apple.com> + + Reviewed by Darin Adler and Mark Rowe. + + https://bugs.webkit.org/show_bug.cgi?id=31200 + Tests in http/tests/security/mixedContent start to fail when new tests are added + + * DumpRenderTree/mac/DumpRenderTree.mm: (dumpRenderTree): Added a workaround for Tiger bug. + +2009-11-11 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Reviewed by Jan Alonzo. + + Create, and display a window for the inspector, for inspector + tests. + + Need to also show/hide the inspector window to avoid having + problems with code assuming it is realized + https://bugs.webkit.org/show_bug.cgi?id=31347 + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (webInspectorShowWindow): + (webInspectorCloseWindow): + (webInspectorInspectWebView): + (createWebView): + +2009-11-11 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Unreviewed Qt buildbot fix. + + My previous fix was wrong, so revert that change and fix it by + returning when the document of the frame has no document element. + Idea is borrowed from mac and win DRT. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::dumpFramesAsText): + +2009-11-11 Eric Seidel <eric@webkit.org> + + Reviewed byg Kenneth Rohde Christiansen. + + Update Kenneth's committer record to include the email he + uses on lists.webkit.org. + + * Scripts/modules/committers.py: + +2009-11-11 Eric Seidel <eric@webkit.org> + + Reviewed by Gustavo Noronha Silva. + + Several committers use separate email addresses for bugzilla and svn.webkit.org + https://bugs.webkit.org/show_bug.cgi?id=31364 + + Update records for existing committers to include email addresses + used in svn.webkit.org and lists.webkit.org. + Most committers use the same email address in all 3 places, but some use + separate addresses. committers.py needs record of each of these addresses. + + * Scripts/modules/committers.py: + +2009-11-11 Eric Seidel <eric@webkit.org> + + No review, adding second email address for an existing committer. + + Add Yong Li's second bugzilla account to his committer record. + See: https://bugs.webkit.org/show_bug.cgi?id=27371#c27 + + * Scripts/modules/committers.py: + +2009-11-11 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + If the frame has no innerText don't append it, and + do not add a newline which breaks some cross platform + results. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::dumpFramesAsText): + +2009-11-11 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Implement missing functionality in the Gtk/Qt TestNetscapePlugin. + + * DumpRenderTree/gtk/TestNetscapePlugin/TestNetscapePlugin.cpp: + (webkit_test_plugin_new_instance): + (webkit_test_plugin_destroy_instance): + (webkit_test_plugin_destroy_stream): + +2009-11-11 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Mark Rowe. + + Implement the functionality needed by plugins/window-open.html + + * DumpRenderTree/gtk/TestNetscapePlugin/TestNetscapePlugin.cpp: + (webkit_test_plugin_new_instance): + (webkit_test_plugin_set_window): + +2009-11-11 Tor Arne Vestbø <tor.arne.vestbo@nokia.com> + + Reviewed by Simon Hausmann. + + [Qt] Force -graphicssystem raster and -style windows when running DRT + + * DumpRenderTree/qt/main.cpp: + * Scripts/run-webkit-tests: + +2009-11-11 Tor Arne Vestbø <tor.arne.vestbo@nokia.com> + + Reviewed by Simon Hausmann. + + Compute correct library paths for Qt + + * Scripts/webkitdirs.pm: + +2009-11-10 Mark Rowe <mrowe@apple.com> + + Reviewed by Sam Weinig. + + <http://webkit.org/b/31200> Tests in http/tests/security/mixedContent start to fail when new tests are added + + The first request to an HTTPS URL results in didFailProvisionalLoadWithError being called with an error + about the validity of the self-signed certificates used in the regression tests. We would then add the + host to the ignore list for SSL certificate errors and retry the request. If this happened during a test + that had enabled frame load delegate logging this would result in extra log messages being generated, + causing the test to fail. + + We address this by explicitly ignoring SSL certificate errors for localhost and 127.0.0.1 before running any + tests. + + * DumpRenderTree/mac/DumpRenderTree.mm: + (dumpRenderTree): + * DumpRenderTree/mac/FrameLoadDelegate.mm: + (-[FrameLoadDelegate webView:didFailProvisionalLoadWithError:forFrame:]): + +2009-11-10 Andras Becsi <becsi.andras@stud.u-szeged.hu> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Remove obsolete scrollbar policy settings from DRT constructor. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::DumpRenderTree): + +2009-11-10 Philippe Normand <pnormand@igalia.com> + + Reviewed by Jan Alonzo. + + [GTK] Remove WebSocket configuration from WebKitWebSettings + https://bugs.webkit.org/show_bug.cgi?id=31244 + + Follow-up of r50724. Don't set the enable-web-socket property + anymore. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (resetDefaultsToConsistentValues): + +2009-11-10 Oliver Hunt <oliver@apple.com> + + Reviewed by Maciej Stachowiak. + + Rename 3D Canvas related classes to use WebGL prefix + https://bugs.webkit.org/show_bug.cgi?id=29095 + + Checkin new version of do-webcore-rename used to do the WebGL type rename, + and upate webkitdirs script to new method of testing for WebGL. + + * Scripts/do-webcore-rename: + * Scripts/webkitdirs.pm: + +2009-11-09 Oliver Hunt <oliver@apple.com> + + Reviewed by Geoff Garen. + + Make do-webcore-rename work with git. + + * Scripts/do-webcore-rename: + +2009-11-09 Simon Hausmann <simon.hausmann@nokia.com> + + Adding Kenneth to the reviewers list. + + * Scripts/modules/committers.py: + +2009-11-09 Martin Robinson <martin.james.robinson@gmail.com> + + Reviewed by Jan Alonzo. + + [GTK] Expose Page::tabKeyCyclesThroughElements in the API + https://bugs.webkit.org/show_bug.cgi?id=30482 + + LayoutTestControllerGtk now uses the exposed + Page::tabKeyCyclesThroughElements API + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::setTabKeyCyclesThroughElements): + +2009-11-08 Shu Chang <Chang.Shu@nokia.com> + + Reviewed by Holger Freyther. + + [Qt] Added support for key code 8 (backspace) in EventSenderQt. + This helps to pass the test case below. Also replaced hardcoded + code numbers with defined constants. + https://bugs.webkit.org/show_bug.cgi?id=31185 + + Test: editing/undo/undo-deleteWord.html + + * DumpRenderTree/qt/EventSenderQt.cpp: + (EventSender::keyDown): + +2009-11-07 Antonio Gomes <tonikitoo@webkit.org> + + Reviewed by Holger Freyther. + + [Qt] [DRT] Fix wrong logic in LayoutTestController processWork + https://bugs.webkit.org/show_bug.cgi?id=31164 + + Fixed wrong logic to assume WorkQueue is done in QT's DRT. + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::processWork): + +2009-11-05 Antonio Gomes <tonikitoo@webkit.org> + + Reviewed by Holger Freyther. + + [Qt][DRT] Replace queueScript by queueNonLoadingScript and queueLoadingScript method + https://bugs.webkit.org/show_bug.cgi?id=31158 + + By invoking a script queue'd by queueScript(), 'true' was beeing returned + always, which from WorkQueue prospective means that a load has been started + and the queue processing should stop and wait for the load to finish. + Spinning it off into a loading and a non-loading variants was the solution + adopted by Mac's DRT to work around this problem. The former keeps returning + 'true' while the later executes the script synchronously and returns 'false' + making it possible to the WorkQueue to proceed right away. + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::processWork): + (LayoutTestController::queueLoadingScript): + (LayoutTestController::queueNonLoadingScript): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + * DumpRenderTree/qt/WorkQueueItem.h: + (LoadingScriptItem::LoadingScriptItem): + (LoadingScriptItem::invoke): + (NonLoadingScriptItem::NonLoadingScriptItem): + (NonLoadingScriptItem::invoke): + +2009-11-07 Mark Rowe <mrowe@apple.com> + + Rubber-stamped by Cameron Zwarich. + + Invoke prepare-ChangeLog via an absolute path rather than assuming it can be found in PATH. + + * Scripts/commit-log-editor: + +2009-11-07 Mark Rowe <mrowe@apple.com> + + Reviewed by Darin Adler. + + Fix <https://bugs.webkit.org/show_bug.cgi?id=28168>. + commit-log-editor does not support all the email address configurations that prepare-Changelog supports + + Move logic for determining the name and email address to use in a ChangeLog entry from + prepare-ChangeLog to VCSUtils so that commit-log-editor can use it. It wants to check + whether the author of the patch matches committer, and therefore needs access to the + email address that would be used in a ChangeLog entry. + + Based on a patch by Pierre d'Herbemont. + + * Scripts/VCSUtils.pm: + * Scripts/commit-log-editor: + * Scripts/prepare-ChangeLog: + * Scripts/webkitdirs.pm: + +2009-11-06 Anantanarayanan G Iyengar <ananta@chromium.org> + + Reviewed by Adam Barth. + + Added functionality to the layout test plugin to invoke document.open and + window.open with default arguments. The associated webkit bug is + https://bugs.webkit.org/show_bug.cgi?id=31067, which affects Chromium. Basically + window.open and document.open calls issued by NPAPI plugins via NPN_Invoke don't + work in Chromium (V8) if there is no calling javascript context. To achieve this + effect we invoke these functions in the layout test plugin in the NPP_SetWindow + for the window.open test case and in NPP_DestroyStream for the document.open test case. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (testDocumentOpen): + (testWindowOpen): + (pluginAllocate): + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h: + * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp: + (NPP_New): + (NPP_SetWindow): + (NPP_DestroyStream): + * DumpRenderTree/win/TestNetscapePlugin/main.cpp: + (NPP_New): + (NPP_SetWindow): + (NPP_NewStream): + (NPP_DestroyStream): + +2009-11-06 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool crashed with exception + https://bugs.webkit.org/show_bug.cgi?id=31092 + + * Scripts/modules/bugzilla.py: Change a ',' to a '%' to fix the error. + +2009-11-04 Eric Seidel <eric@webkit.org> + + No review, just add a line which got left out of the patch uploaded for commit. + + commit-queue is crashing when trying to reject patches after unknown failures + https://bugs.webkit.org/show_bug.cgi?id=31091 + + * Scripts/bugzilla-tool: Set bug_log = None after closing to we don't re-close and crash. + +2009-11-04 Adam Roben <aroben@apple.com> + + Remove bogus else clause in bugzilla-tool + + Fixes <http://webkit.org/b/31125> REGRESSION (r47121): bugzilla-tool + create-bug raises exception after entering bug description + + Reviewed by David Kilzer. + + * Scripts/bugzilla-tool: + (CreateBug): else clauses are only hit if no exception is raised, so + it makes no sense to try to re-raise the exception in an else clause. + The exception will automatically be re-raised if it doesn't match any + of the except clauses, so we don't have to do anything special here at + all to get the desired behavior. + +2009-11-04 Eric Seidel <eric@webkit.org> + + Reviewed by David Kilzer. + + svn-apply's fixChangeLogPatch function seems broken + https://bugs.webkit.org/show_bug.cgi?id=30683 + + Update fixChangeLogPatch to be able to handle patches which + don't start at line 1. + Add unit tests for svn-apply to scm_unittest.py. + + * Scripts/VCSUtils.pm: + * Scripts/modules/scm_unittest.py: + +2009-11-04 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Beth Dakin. + + Need to implement ARIA role="combobox" + https://bugs.webkit.org/show_bug.cgi?id=31096 + + * DumpRenderTree/AccessibilityUIElement.cpp: + (showMenuCallback): + (getIsExpandedCallback): + (AccessibilityUIElement::getJSClass): + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::isExpanded): + (AccessibilityUIElement::showMenu): + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::isExpanded): + (AccessibilityUIElement::showMenu): + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::isSelected): + (AccessibilityUIElement::isExpanded): + (AccessibilityUIElement::showMenu): + +2009-11-04 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + commit-queue is crashing when trying to reject patches after unknown failures + https://bugs.webkit.org/show_bug.cgi?id=31091 + + * Scripts/bugzilla-tool: + - patch['id'] was a copy/paste mistake. This code has no 'patch' variable + so we have to find out what the current patch is by asking bugzilla again. + - Discovered that this code was also leaking file descriptors, so fixed that. + +2009-11-04 Adam Roben <aroben@apple.com> + + Make run-webkit-tests work for the Debug_Internal Windows + configuration + + In Debug_Internal, DumpRenderTree.exe and ImageDiff.exe have no _debug + suffix. + + Fixes <http://webkit.org/b/31123>. + + Reviewed by Sam Weinig. + + * Scripts/run-webkit-tests: Don't add the _debug suffix in + Debug_Internal, either. + +2009-11-04 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + committers.py needs a way to store non-bugzilla email addresses + https://bugs.webkit.org/show_bug.cgi?id=31037 + + Make Committer and Reviewer constructors take a single email or a list of emails. + Change committer_by_bugzilla_email functions to committer_by_email to support lookup by any email. + Expose reviewers(), used by validate-committer-lists on bug 30970. + + * Scripts/modules/committers.py: + * Scripts/modules/committers_unittest.py: Added tests for the new code. + +2009-11-03 Yuzo Fujishima <yuzo@google.com> + + Reviewed by David Levin. + + Start/Stop Web Socket and Web Socket Secure servers for layout tests. + https://bugs.webkit.org/show_bug.cgi?id=27491 + + The test path determination logic is changed to handle websocket and websocket/ssl cases. + The logic for non-http (and now also non-websocket) tests is moved toward the end of the if-elsif statement. + + Functions to start or stop Web Socket servers are added. + + * Scripts/run-webkit-tests: + +2009-11-03 Eric Seidel <eric@webkit.org> + + No review, just changing wording of log message. + + Change log string to say "failed" instead of "rejected" + when a commit fails due to an out of date checkout. + This makes grepping the commit-queue log for rejected patches easier. + + * Scripts/bugzilla-tool: + +2009-11-03 Eric Seidel <eric@webkit.org> + + No review, adding commonly known committers missing from the file. + + * Scripts/modules/committers.py: Add committers found by looking at SVN records. + +2009-11-03 Eric Seidel <eric@webkit.org> + + No review, just sort-order cleanup. + + * Scripts/modules/committers.py: Sort committers/reviewers alphabetically. + +2009-11-03 Stephen White <senorblanco@chromium.org> + + (Unreviewed). + + Add myself to committers.py. + + * Scripts/modules/committers.py: + +2009-11-03 Robin Dunn <robin@alldunn.com> + + Reviewed by Kevin Ollviier. + + Add packaging scripts for Debian-based Linux distros. + + https://bugs.webkit.org/show_bug.cgi?id=31075 + + * wx/packaging/build-debian-installer.py: Added. + * wx/packaging/debian: Added. + +2009-11-03 Dan Bernstein <mitz@apple.com> + + Reviewed by Anders Carlsson and Beth Dakin. + + https://bugs.webkit.org/show_bug.cgi?id=31070 + + Added an 'ondestroy' parameter to the test plug-in. When the plug-in is + destroyed, it executes the value of the 'ondestroy' parameter as a + script. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (pluginAllocate): Initialize onDestroy. + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h: Define + onDestroy. + * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp: + (NPP_New): Set onDestroy to the value of the 'ondestroy' parameter, if + specified. + (NPP_Destroy): Execute the value of 'ondestroy' as a script. + +2009-11-02 Joanmarie Diggs <joanmarie.diggs@gmail.com> + + Reviewed by Xan Lopez. + + https://bugs.webkit.org/show_bug.cgi?id=31035 + [GTK] some accessibility tests hitting assertion in debug builds + + Get the correct Gtk+ object before attempting to turn it into an AtkObject. + + * DumpRenderTree/gtk/AccessibilityControllerGtk.cpp: + (AccessibilityController::rootElement): + +2009-11-02 Chris Fleizach <cfleizach@apple.com> + + Reviewed by Beth Dakin. + + Support ARIA "tab" roles + https://bugs.webkit.org/show_bug.cgi?id=30842 + + * DumpRenderTree/AccessibilityUIElement.cpp: + * DumpRenderTree/AccessibilityUIElement.h: + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + +2009-11-01 Eric Seidel <eric@webkit.org> + + Reviewed by David Levin. + + buildbots should use --exit-after-N-failures + https://bugs.webkit.org/show_bug.cgi?id=30809 + + Make the bots exit after 20 failures to prevent never-ending + test runs where every test spends a minute crashing. + + * BuildSlaveSupport/build.webkit.org-config/master.cfg: + +2009-11-01 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Eric Seidel. + + Turn on warnings for QtWebKit for gcc + https://bugs.webkit.org/show_bug.cgi?id=30958 + + * DumpRenderTree/qt/main.cpp: + (crashHandler): Mark function NO_RETURN + +2009-11-01 Jessie Berlin <jberlin@webkit.org> + + Adding myself to the committers list. + + * Scripts/modules/committers.py: + +2009-10-30 Jon Honeycutt <jhoneycutt@apple.com> + + Fix an issue that Adam noticed in DRT. + + Reviewed by Darin Adler. + + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::role): + Get the length of the role text, and create a buffer dynamically. + +2009-10-30 Eric Seidel <eric@webkit.org> + + No review, rolling out r50105. + http://trac.webkit.org/changeset/50105 + + This commit was causing: + https://bugs.webkit.org/show_bug.cgi?id=30869 + We'll re-implement the feature a different way. + + * Scripts/bugzilla-tool: + +2009-10-29 Jon Honeycutt <jhoneycutt@apple.com> + + Test for MSAA: Accessibility of headings is not correct + + https://bugs.webkit.org/show_bug.cgi?id=30937 + + Reviewed by Adam Roben. + + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::role): + Allow the role returned to be a BSTR. + (AccessibilityUIElement::description): + Fix a copy/paste error. + +2009-10-29 Jon Honeycutt <jhoneycutt@apple.com> + + Test for MSAA: Accessibility of links is wrong + + https://bugs.webkit.org/show_bug.cgi?id=30928 + + Reviewed by Darin Adler. + + * DumpRenderTree/AccessibilityUIElement.cpp: + (getAccessibilityValueCallback): + Return the accessibility value. + (AccessibilityUIElement::getJSClass): + Added "accessibilityValue" value. + + * DumpRenderTree/AccessibilityUIElement.h: + + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::accessibilityValue): + Stubbed. + + * DumpRenderTree/mac/AccessibilityUIElementMac.mm: + (AccessibilityUIElement::accessibilityValue): + Stubbed. + + * DumpRenderTree/win/AccessibilityUIElementWin.cpp: + (AccessibilityUIElement::accessibilityValue): + Get the object's value, and return it as a JS string. + +2009-10-30 Kevin Ollivier <kevino@theolliviers.com> + + Fix typo in command name used by wx build system. + + * wx/build/build_utils.py: + +2009-10-30 Adam Barth <abarth@webkit.org> + + Reviewed by Darin Adler. + + Patch v1 is a dumb default name for patches + https://bugs.webkit.org/show_bug.cgi?id=30952 + + Let's use "Patch" instead. + + * Scripts/bugzilla-tool: + +2009-10-30 Andras Becsi <becsi.andras@stud.u-szeged.hu> + + Unreviewed trivial buildfix. + + [Qt] Buildfix for r50333. + + * DumpRenderTree/qt/DumpRenderTree.pro: + +2009-10-30 Antonio Gomes <tonikitoo@webkit.org> + + Reviewed by Holger Freyther. + + [Qt] Remove qt/WorkQueue.cpp|h in favor of platform independent WorkQueue + https://bugs.webkit.org/show_bug.cgi?id=30953 + + DumpRenderTree/WorkQueue and DumpRenderTree/qt/WorkQueue share mostly the + same implementation. Some Q_ASSERTs differ from ASSERTs basically. Patch + makes qt DRT to share this implementation (as gtk and mac ports do). + + * DumpRenderTree/qt/DumpRenderTree.pro: + * DumpRenderTree/qt/WorkQueue.cpp: Removed. + * DumpRenderTree/qt/WorkQueue.h: Removed. + +2009-10-30 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Unreviewed potential buildbot fix. + + Second try: Reset page history before running each test. + + Apparently the QWebHistory::clear() keeps the current page + in history which is not what we want, so we not additionally + sets the history capacity to 0 (forces removing everything) + and then sets it back to its original value. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-10-30 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Unreviewed potential buildbot fix. + + Reset page history before running each test. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + +2009-10-30 Roland Steiner <rolandsteiner@chromium.org> + + Reviewed by Eric Seidel. + + Remove ENABLE_RUBY guards as discussed with Dave Hyatt and Maciej Stachowiak. + + Bug 28420 - Implement HTML5 <ruby> rendering + (https://bugs.webkit.org/show_bug.cgi?id=28420) + + No new tests (no functional change). + + * Scripts/build-webkit: + +2009-10-29 Dan Bernstein <mitz@apple.com> + + Reviewed by Mark Rowe. + + Fix “Undefined subroutine” errors in svn-*apply by moving the removeEOL subroutine + from the two scripts that define it but don’t use it to the script that uses it but doesn’t + define it. + + * Scripts/VCSUtils.pm: + * Scripts/svn-apply: + * Scripts/svn-unapply: + +2009-10-29 Xan Lopez <xlopez@igalia.com> + + Reviewed by Gustavo Noronha. + + Use the GTK+ main loop instead of rolling our own mini-version of + it. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (dump): + (runTest): + (webViewLoadFinished): + +2009-10-29 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Oliver Hunt. + + Implement the Qt version of DRT dumpBackForwardList(). + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::dumpHistoryItem): + (WebCore::DumpRenderTree::dumpBackForwardList): + +2009-10-29 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Adam Roben. + + Implement DRT functionality for Qt introduced in + r28690 and r28705. + + - Implemented pathToLocalResource which exposes the functionality of + converting a given unix path to the correct location on Windows. + - Implemented a way to remove machine-dependent information from paths + in layout test results. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::urlSuitableForTestResult): + (WebCore::WebPage::javaScriptConsoleMessage): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::pathToLocalResource): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + 2009-10-28 Roland Steiner <rolandsteiner@chromium.org> Adding myself to the committers list. diff --git a/WebKitTools/DumpRenderTree/AccessibilityController.cpp b/WebKitTools/DumpRenderTree/AccessibilityController.cpp index af1daf6..d3688f4 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityController.cpp +++ b/WebKitTools/DumpRenderTree/AccessibilityController.cpp @@ -48,7 +48,11 @@ static JSValueRef getRootElementCallback(JSContextRef context, JSObjectRef thisO void AccessibilityController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception) { JSRetainPtr<JSStringRef> accessibilityControllerStr(Adopt, JSStringCreateWithUTF8CString("accessibilityController")); - JSValueRef accessibilityControllerObject = JSObjectMake(context, getJSClass(), this); + + JSClassRef classRef = getJSClass(); + JSValueRef accessibilityControllerObject = JSObjectMake(context, classRef, this); + JSClassRelease(classRef); + JSObjectSetProperty(context, windowObject, accessibilityControllerStr.get(), accessibilityControllerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception); } @@ -85,8 +89,7 @@ JSClassRef AccessibilityController::getJSClass() 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static JSClassRef accessibilityControllerClass = JSClassCreate(&classDefinition); - return accessibilityControllerClass; + return JSClassCreate(&classDefinition); } void AccessibilityController::resetToConsistentState() diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp index 5958ccb..8c59252 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp +++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp @@ -161,6 +161,53 @@ static JSValueRef childAtIndexCallback(JSContextRef context, JSObjectRef functio return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->getChildAtIndex(indexNumber)); } +static JSValueRef disclosedRowAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + int indexNumber = 0; + if (argumentCount == 1) + indexNumber = JSValueToNumber(context, arguments[0], exception); + + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->disclosedRowAtIndex(indexNumber)); +} + +static JSValueRef ariaOwnsElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + int indexNumber = 0; + if (argumentCount == 1) + indexNumber = JSValueToNumber(context, arguments[0], exception); + + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->ariaOwnsElementAtIndex(indexNumber)); +} + +static JSValueRef ariaFlowToElementAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + int indexNumber = 0; + if (argumentCount == 1) + indexNumber = JSValueToNumber(context, arguments[0], exception); + + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->ariaFlowToElementAtIndex(indexNumber)); +} + +static JSValueRef selectedRowAtIndexCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + int indexNumber = 0; + if (argumentCount == 1) + indexNumber = JSValueToNumber(context, arguments[0], exception); + + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->selectedRowAtIndex(indexNumber)); +} + +static JSValueRef isEqualCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + JSObjectRef otherElement = 0; + if (argumentCount == 1) + otherElement = JSValueToObject(context, arguments[0], exception); + else + return JSValueMakeBoolean(context, false); + + return JSValueMakeBoolean(context, toAXElement(thisObject)->isEqual(toAXElement(otherElement))); +} + static JSValueRef elementAtPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { int x = 0; @@ -173,13 +220,23 @@ static JSValueRef elementAtPointCallback(JSContextRef context, JSObjectRef funct return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->elementAtPoint(x, y)); } +static JSValueRef isAttributeSupportedCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + JSStringRef attribute = 0; + if (argumentCount == 1) + attribute = JSValueToStringCopy(context, arguments[0], exception); + JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->isAttributeSupported(attribute)); + if (attribute) + JSStringRelease(attribute); + return result; +} static JSValueRef isAttributeSettableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - JSStringRef attribute = NULL; + JSStringRef attribute = 0; if (argumentCount == 1) attribute = JSValueToStringCopy(context, arguments[0], exception); - JSValueRef result = JSValueMakeNumber(context, toAXElement(thisObject)->isAttributeSettable(attribute)); + JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->isAttributeSettable(attribute)); if (attribute) JSStringRelease(attribute); return result; @@ -191,7 +248,7 @@ static JSValueRef isActionSupportedCallback(JSContextRef context, JSObjectRef fu JSStringRef action = 0; if (argumentCount == 1) action = JSValueToStringCopy(context, arguments[0], exception); - JSValueRef result = JSValueMakeNumber(context, toAXElement(thisObject)->isActionSupported(action)); + JSValueRef result = JSValueMakeBoolean(context, toAXElement(thisObject)->isActionSupported(action)); if (action) JSStringRelease(action); return result; @@ -199,7 +256,7 @@ static JSValueRef isActionSupportedCallback(JSContextRef context, JSObjectRef fu static JSValueRef attributeValueCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - JSStringRef attribute = NULL; + JSStringRef attribute = 0; if (argumentCount == 1) attribute = JSValueToStringCopy(context, arguments[0], exception); JSRetainPtr<JSStringRef> attributeValue(Adopt, toAXElement(thisObject)->attributeValue(attribute)); @@ -230,6 +287,11 @@ static JSValueRef parentElementCallback(JSContextRef context, JSObjectRef functi return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->parentElement()); } +static JSValueRef disclosedByRowCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, toAXElement(thisObject)->disclosedByRow()); +} + static JSValueRef setSelectedTextRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { unsigned location = UINT_MAX, length = 0; @@ -239,24 +301,51 @@ static JSValueRef setSelectedTextRangeCallback(JSContextRef context, JSObjectRef } toAXElement(thisObject)->setSelectedTextRange(location, length); - return 0; + return JSValueMakeUndefined(context); } static JSValueRef incrementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { toAXElement(thisObject)->increment(); - return 0; + return JSValueMakeUndefined(context); } static JSValueRef decrementCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { toAXElement(thisObject)->decrement(); - return 0; + return JSValueMakeUndefined(context); } +static JSValueRef showMenuCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + toAXElement(thisObject)->showMenu(); + return JSValueMakeUndefined(context); +} // Static Value Getters +static JSValueRef getARIADropEffectsCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> dropEffects(Adopt, toAXElement(thisObject)->ariaDropEffects()); + return JSValueMakeString(context, dropEffects.get()); +} + +static JSValueRef getARIAIsGrabbedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + return JSValueMakeBoolean(context, toAXElement(thisObject)->ariaIsGrabbed()); +} + +static JSValueRef getIsValidCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + AccessibilityUIElement* uiElement = toAXElement(thisObject); + if (!uiElement->platformUIElement()) + return JSValueMakeBoolean(context, false); + + // There might be other platform logic that one could check here... + + return JSValueMakeBoolean(context, true); +} + static JSValueRef getRoleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { JSRetainPtr<JSStringRef> role(Adopt, toAXElement(thisObject)->role()); @@ -269,6 +358,12 @@ static JSValueRef getSubroleCallback(JSContextRef context, JSObjectRef thisObjec return JSValueMakeString(context, role.get()); } +static JSValueRef getRoleDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> roleDesc(Adopt, toAXElement(thisObject)->roleDescription()); + return JSValueMakeString(context, roleDesc.get()); +} + static JSValueRef getTitleCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { JSRetainPtr<JSStringRef> title(Adopt, toAXElement(thisObject)->title()); @@ -281,12 +376,24 @@ static JSValueRef getDescriptionCallback(JSContextRef context, JSObjectRef thisO return JSValueMakeString(context, description.get()); } +static JSValueRef getStringValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> stringValue(Adopt, toAXElement(thisObject)->stringValue()); + return JSValueMakeString(context, stringValue.get()); +} + static JSValueRef getLanguageCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { JSRetainPtr<JSStringRef> language(Adopt, toAXElement(thisObject)->language()); return JSValueMakeString(context, language.get()); } +static JSValueRef getOrientationCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> orientation(Adopt, toAXElement(thisObject)->orientation()); + return JSValueMakeString(context, orientation.get()); +} + static JSValueRef getChildrenCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { return JSValueMakeNumber(context, toAXElement(thisObject)->childrenCount()); @@ -358,12 +465,45 @@ static JSValueRef getIsRequiredCallback(JSContextRef context, JSObjectRef thisOb return JSValueMakeBoolean(context, toAXElement(thisObject)->isRequired()); } +static JSValueRef getIsSelectedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*) +{ + return JSValueMakeBoolean(context, toAXElement(thisObject)->isSelected()); +} + +static JSValueRef getIsExpandedCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*) +{ + return JSValueMakeBoolean(context, toAXElement(thisObject)->isExpanded()); +} + +static JSValueRef hierarchicalLevelCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef, JSValueRef*) +{ + return JSValueMakeNumber(context, toAXElement(thisObject)->hierarchicalLevel()); +} + static JSValueRef getValueDescriptionCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { JSRetainPtr<JSStringRef> valueDescription(Adopt, toAXElement(thisObject)->valueDescription()); return JSValueMakeString(context, valueDescription.get()); } +static JSValueRef getAccessibilityValueCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> accessibilityValue(Adopt, toAXElement(thisObject)->accessibilityValue()); + return JSValueMakeString(context, accessibilityValue.get()); +} + +static JSValueRef getDocumentEncodingCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> documentEncoding(Adopt, toAXElement(thisObject)->documentEncoding()); + return JSValueMakeString(context, documentEncoding.get()); +} + +static JSValueRef getDocumentURICallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr<JSStringRef> documentURI(Adopt, toAXElement(thisObject)->documentURI()); + return JSValueMakeString(context, documentURI.get()); +} + // Destruction static void finalize(JSObjectRef thisObject) @@ -381,11 +521,14 @@ JSObjectRef AccessibilityUIElement::makeJSAccessibilityUIElement(JSContextRef co JSClassRef AccessibilityUIElement::getJSClass() { static JSStaticValue staticValues[] = { + { "accessibilityValue", getAccessibilityValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "role", getRoleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "subrole", getSubroleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "roleDescription", getRoleDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "title", getTitleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "description", getDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "language", getLanguageCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "stringValue", getStringValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "x", getXCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "y", getYCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "width", getWidthCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -400,7 +543,16 @@ JSClassRef AccessibilityUIElement::getJSClass() { "selectedTextRange", getSelectedTextRangeCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isEnabled", getIsEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isRequired", getIsRequiredCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "isSelected", getIsSelectedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "isExpanded", getIsExpandedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "valueDescription", getValueDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "hierarchicalLevel", hierarchicalLevelCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "documentEncoding", getDocumentEncodingCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "documentURI", getDocumentURICallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "isValid", getIsValidCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "orientation", getOrientationCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "ariaIsGrabbed", getARIAIsGrabbedCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "ariaDropEffects", getARIADropEffectsCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { 0, 0, 0, 0 } }; @@ -428,11 +580,19 @@ JSClassRef AccessibilityUIElement::getJSClass() { "titleUIElement", titleUIElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setSelectedTextRange", setSelectedTextRangeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "attributeValue", attributeValueCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "isAttributeSupported", isAttributeSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isAttributeSettable", isAttributeSettableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isActionSupported", isActionSupportedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "parentElement", parentElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "disclosedByRow", disclosedByRowCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "increment", incrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "decrement", decrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "showMenu", showMenuCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "disclosedRowAtIndex", disclosedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "ariaOwnsElementAtIndex", ariaOwnsElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "ariaFlowToElementAtIndex", ariaFlowToElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "selectedRowAtIndex", selectedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "isEqual", isEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { 0, 0, 0 } }; diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h index fffdad8..9985523 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h +++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h @@ -61,6 +61,8 @@ public: static JSObjectRef makeJSAccessibilityUIElement(JSContextRef, const AccessibilityUIElement&); + bool isEqual(AccessibilityUIElement* otherElement) { return platformUIElement() == otherElement->platformUIElement(); } + void getLinkedUIElements(Vector<AccessibilityUIElement>&); void getDocumentLinks(Vector<AccessibilityUIElement>&); void getChildren(Vector<AccessibilityUIElement>&); @@ -80,16 +82,22 @@ public: JSStringRef parameterizedAttributeNames(); void increment(); void decrement(); + void showMenu(); // Attributes - platform-independent implementations JSStringRef attributeValue(JSStringRef attribute); + bool isAttributeSupported(JSStringRef attribute); bool isAttributeSettable(JSStringRef attribute); bool isActionSupported(JSStringRef action); JSStringRef role(); JSStringRef subrole(); + JSStringRef roleDescription(); JSStringRef title(); JSStringRef description(); JSStringRef language(); + JSStringRef stringValue(); + JSStringRef accessibilityValue() const; + JSStringRef orientation() const; double x(); double y(); double width(); @@ -102,8 +110,13 @@ public: JSStringRef selectedTextRange(); bool isEnabled(); bool isRequired() const; + bool isSelected() const; + bool isExpanded() const; + int hierarchicalLevel() const; double clickPointX(); double clickPointY(); + JSStringRef documentEncoding(); + JSStringRef documentURI(); // Table-specific attributes JSStringRef attributesOfColumnHeaders(); @@ -116,6 +129,20 @@ public: JSStringRef rowIndexRange(); JSStringRef columnIndexRange(); + // Tree/Outline specific attributes + AccessibilityUIElement selectedRowAtIndex(unsigned); + AccessibilityUIElement disclosedByRow(); + AccessibilityUIElement disclosedRowAtIndex(unsigned); + + // ARIA specific + AccessibilityUIElement ariaOwnsElementAtIndex(unsigned); + AccessibilityUIElement ariaFlowToElementAtIndex(unsigned); + + // ARIA Drag and Drop + bool ariaIsGrabbed() const; + // A space concatentated string of all the drop effects. + JSStringRef ariaDropEffects() const; + // Parameterized attributes int lineForIndex(int); JSStringRef boundsForRange(unsigned location, unsigned length); diff --git a/WebKitTools/DumpRenderTree/GCController.cpp b/WebKitTools/DumpRenderTree/GCController.cpp index fe84a58..06a04fb 100644 --- a/WebKitTools/DumpRenderTree/GCController.cpp +++ b/WebKitTools/DumpRenderTree/GCController.cpp @@ -74,28 +74,15 @@ static JSValueRef getJSObjectCountCallback(JSContextRef context, JSObjectRef fun void GCController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception) { JSRetainPtr<JSStringRef> gcControllerStr(Adopt, JSStringCreateWithUTF8CString("GCController")); - JSValueRef gcControllerObject = JSObjectMake(context, getJSClass(), this); - JSObjectSetProperty(context, windowObject, gcControllerStr.get(), gcControllerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception); -} - -JSClassRef GCController::getJSClass() -{ - static JSClassRef gcControllerClass = 0; - if (!gcControllerClass) { - JSStaticFunction* staticFunctions = GCController::staticFunctions(); - JSClassDefinition classDefinition = { - 0, kJSClassAttributeNone, "GCController", 0, 0, staticFunctions, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; + JSClassRef classRef = getJSClass(); + JSValueRef gcControllerObject = JSObjectMake(context, classRef, this); + JSClassRelease(classRef); - gcControllerClass = JSClassCreate(&classDefinition); - } - - return gcControllerClass; + JSObjectSetProperty(context, windowObject, gcControllerStr.get(), gcControllerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception); } -JSStaticFunction* GCController::staticFunctions() +JSClassRef GCController::getJSClass() { static JSStaticFunction staticFunctions[] = { { "collect", collectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -104,5 +91,10 @@ JSStaticFunction* GCController::staticFunctions() { 0, 0, 0 } }; - return staticFunctions; + static JSClassDefinition classDefinition = { + 0, kJSClassAttributeNone, "GCController", 0, 0, staticFunctions, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + return JSClassCreate(&classDefinition); } diff --git a/WebKitTools/DumpRenderTree/GCController.h b/WebKitTools/DumpRenderTree/GCController.h index 4284275..afc1de0 100644 --- a/WebKitTools/DumpRenderTree/GCController.h +++ b/WebKitTools/DumpRenderTree/GCController.h @@ -45,7 +45,6 @@ public: private: static JSClassRef getJSClass(); - static JSStaticFunction* staticFunctions(); }; #endif // GCController_h diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp index daf888f..572d610 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp +++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp @@ -838,6 +838,18 @@ static JSValueRef setXSSAuditorEnabledCallback(JSContextRef context, JSObjectRef return JSValueMakeUndefined(context); } +static JSValueRef setAllowUniversalAccessFromFileURLsCallback(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->setAllowUniversalAccessFromFileURLs(JSValueToBoolean(context, arguments[0])); + + return JSValueMakeUndefined(context); +} + static JSValueRef setTabKeyCyclesThroughElementsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has mac & windows implementation @@ -850,6 +862,16 @@ static JSValueRef setTabKeyCyclesThroughElementsCallback(JSContextRef context, J return JSValueMakeUndefined(context); } +static JSValueRef setTimelineProfilingEnabledCallback(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->setTimelineProfilingEnabled(JSValueToBoolean(context, arguments[0])); + return JSValueMakeUndefined(context); +} + static JSValueRef setUseDashboardCompatibilityModeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has mac implementation @@ -979,6 +1001,7 @@ static JSValueRef showWebInspectorCallback(JSContextRef context, JSObjectRef fun static JSValueRef closeWebInspectorCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); + controller->setTimelineProfilingEnabled(false); controller->closeWebInspector(); return JSValueMakeUndefined(context); } @@ -1050,6 +1073,22 @@ static JSValueRef pauseTransitionAtTimeOnElementWithIdCallback(JSContextRef cont return JSValueMakeBoolean(context, controller->pauseTransitionAtTimeOnElementWithId(propertyName.get(), time, elementId.get())); } +static JSValueRef sampleSVGAnimationForElementAtTimeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount != 3) + return JSValueMakeUndefined(context); + + JSRetainPtr<JSStringRef> animationId(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + ASSERT(!*exception); + double time = JSValueToNumber(context, arguments[1], exception); + ASSERT(!*exception); + JSRetainPtr<JSStringRef> elementId(Adopt, JSValueToStringCopy(context, arguments[2], exception)); + ASSERT(!*exception); + + LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); + return JSValueMakeBoolean(context, controller->sampleSVGAnimationForElementAtTime(animationId.get(), time, elementId.get())); +} + static JSValueRef numberOfActiveAnimationsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { if (argumentCount != 0) @@ -1150,26 +1189,24 @@ void LayoutTestController::makeWindowObject(JSContextRef context, JSObjectRef wi { JSRetainPtr<JSStringRef> layoutTestContollerStr(Adopt, JSStringCreateWithUTF8CString("layoutTestController")); ref(); - JSValueRef layoutTestContollerObject = JSObjectMake(context, getJSClass(), this); + + JSClassRef classRef = getJSClass(); + JSValueRef layoutTestContollerObject = JSObjectMake(context, classRef, this); + JSClassRelease(classRef); + JSObjectSetProperty(context, windowObject, layoutTestContollerStr.get(), layoutTestContollerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception); } JSClassRef LayoutTestController::getJSClass() { - static JSClassRef layoutTestControllerClass; - - if (!layoutTestControllerClass) { - JSStaticValue* staticValues = LayoutTestController::staticValues(); - JSStaticFunction* staticFunctions = LayoutTestController::staticFunctions(); - JSClassDefinition classDefinition = { - 0, kJSClassAttributeNone, "LayoutTestController", 0, staticValues, staticFunctions, - 0, layoutTestControllerObjectFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - layoutTestControllerClass = JSClassCreate(&classDefinition); - } + static JSStaticValue* staticValues = LayoutTestController::staticValues(); + static JSStaticFunction* staticFunctions = LayoutTestController::staticFunctions(); + static JSClassDefinition classDefinition = { + 0, kJSClassAttributeNone, "LayoutTestController", 0, staticValues, staticFunctions, + 0, layoutTestControllerObjectFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; - return layoutTestControllerClass; + return JSClassCreate(&classDefinition); } JSStaticValue* LayoutTestController::staticValues() @@ -1227,6 +1264,7 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "pathToLocalResource", pathToLocalResourceCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "pauseAnimationAtTimeOnElementWithId", pauseAnimationAtTimeOnElementWithIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "pauseTransitionAtTimeOnElementWithId", pauseTransitionAtTimeOnElementWithIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "sampleSVGAnimationForElementAtTime", sampleSVGAnimationForElementAtTimeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "printToPDF", dumpAsPDFCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "queueBackNavigation", queueBackNavigationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "queueForwardNavigation", queueForwardNavigationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1237,6 +1275,7 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "removeAllVisitedLinks", removeAllVisitedLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setAllowUniversalAccessFromFileURLs", setAllowUniversalAccessFromFileURLsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAlwaysAcceptCookies", setAlwaysAcceptCookiesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAppCacheMaximumSize", setAppCacheMaximumSizeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAuthenticationPassword", setAuthenticationPasswordCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1264,6 +1303,7 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "setSmartInsertDeleteEnabled", setSmartInsertDeleteEnabledCallback, 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 }, diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h index 79ffb99..7e18dce 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.h +++ b/WebKitTools/DumpRenderTree/LayoutTestController.h @@ -67,6 +67,7 @@ public: void removeAllVisitedLinks(); void setAcceptsEditing(bool acceptsEditing); void setAppCacheMaximumSize(unsigned long long quota); + void setAllowUniversalAccessFromFileURLs(bool); void setAuthorAndUserStylesEnabled(bool); void setCacheModel(int); void setCustomPolicyDelegate(bool setDelegate, bool permissive); @@ -201,6 +202,7 @@ public: bool pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId); bool pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId); + bool sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId); unsigned numberOfActiveAnimations() const; void whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains); @@ -214,6 +216,7 @@ public: void showWebInspector(); void closeWebInspector(); + void setTimelineProfilingEnabled(bool enabled); void evaluateInWebInspector(long callId, JSStringRef script); void evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script); diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp index 14280ba..fa8aed1 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp @@ -34,25 +34,12 @@ #include <string.h> #include <stdlib.h> -void pluginLog(NPP instance, const char* format, ...) +// Helper function which takes in the plugin window object for logging to the console object. +static void pluginLogWithWindowObject(NPObject* windowObject, NPP instance, const char* message) { - va_list args; - va_start(args, format); - char message[2048] = "PLUGIN: "; - vsprintf(message + strlen(message), format, args); - va_end(args); - - NPObject* windowObject = 0; - NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject); - if (error != NPERR_NO_ERROR) { - fprintf(stderr, "Failed to retrieve window object while logging: %s\n", message); - return; - } - NPVariant consoleVariant; if (!browser->getproperty(instance, windowObject, browser->getstringidentifier("console"), &consoleVariant)) { fprintf(stderr, "Failed to retrieve console object while logging: %s\n", message); - browser->releaseobject(windowObject); return; } @@ -65,12 +52,43 @@ void pluginLog(NPP instance, const char* format, ...) if (!browser->invoke(instance, consoleObject, browser->getstringidentifier("log"), &messageVariant, 1, &result)) { fprintf(stderr, "Failed to invoke console.log while logging: %s\n", message); browser->releaseobject(consoleObject); - browser->releaseobject(windowObject); return; } browser->releasevariantvalue(&result); browser->releaseobject(consoleObject); +} + +// Helper function which takes in the plugin window object for logging to the console object. This function supports variable +// arguments. +static void pluginLogWithWindowObjectVariableArgs(NPObject* windowObject, NPP instance, const char* format, ...) +{ + va_list args; + va_start(args, format); + char message[2048] = "PLUGIN: "; + vsprintf(message + strlen(message), format, args); + va_end(args); + + pluginLogWithWindowObject(windowObject, instance, message); +} + +// Helper function to log to the console object. +void pluginLog(NPP instance, const char* format, ...) +{ + va_list args; + va_start(args, format); + char message[2048] = "PLUGIN: "; + vsprintf(message + strlen(message), format, args); + va_end(args); + + NPObject* windowObject = 0; + NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject); + if (error != NPERR_NO_ERROR) { + fprintf(stderr, "Failed to retrieve window object while logging: %s\n", message); + return; + } + + pluginLogWithWindowObject(windowObject, instance, message); browser->releaseobject(windowObject); } @@ -152,6 +170,8 @@ enum { ID_TEST_THROW_EXCEPTION_METHOD, ID_TEST_FAIL_METHOD, ID_DESTROY_NULL_STREAM, + ID_TEST_RELOAD_PLUGINS_NO_PAGES, + ID_TEST_RELOAD_PLUGINS_AND_PAGES, NUM_METHOD_IDENTIFIERS }; @@ -177,7 +197,9 @@ static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = { "testConstruct", "testThrowException", "testFail", - "destroyNullStream" + "destroyNullStream", + "reloadPluginsNoPages", + "reloadPluginsAndPages" }; static NPUTF8* createCStringFromNPVariant(const NPVariant* variant) @@ -643,6 +665,76 @@ static bool testConstruct(PluginObject* obj, const NPVariant* args, uint32_t arg return browser->construct(obj->npp, NPVARIANT_TO_OBJECT(args[0]), args + 1, argCount - 1, result); } +// Helper function to notify the layout test controller that the test completed. +void notifyTestCompletion(NPP npp, NPObject* object) +{ + NPVariant result; + NPString script; + script.UTF8Characters = "javascript:window.layoutTestController.notifyDone();"; + script.UTF8Length = strlen("javascript:window.layoutTestController.notifyDone();"); + browser->evaluate(npp, object, &script, &result); + browser->releasevariantvalue(&result); +} + +bool testDocumentOpen(NPP npp) +{ + NPIdentifier documentId = browser->getstringidentifier("document"); + NPIdentifier openId = browser->getstringidentifier("open"); + + NPObject *windowObject = NULL; + browser->getvalue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return false; + + NPVariant docVariant; + browser->getproperty(npp, windowObject, documentId, &docVariant); + if (docVariant.type != NPVariantType_Object) + return false; + + NPObject *documentObject = NPVARIANT_TO_OBJECT(docVariant); + + NPVariant openArgs[2]; + STRINGZ_TO_NPVARIANT("text/html", openArgs[0]); + STRINGZ_TO_NPVARIANT("_blank", openArgs[1]); + + NPVariant result; + browser->invoke(npp, documentObject, openId, openArgs, 2, &result); + 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; + } + + return false; +} + +bool testWindowOpen(NPP npp) +{ + NPIdentifier openId = browser->getstringidentifier("open"); + + NPObject *windowObject = NULL; + browser->getvalue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return false; + + NPVariant openArgs[2]; + STRINGZ_TO_NPVARIANT("about:blank", openArgs[0]); + 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; + } + return false; +} + static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { PluginObject* plugin = reinterpret_cast<PluginObject*>(header); @@ -691,6 +783,13 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a browser->invoke(plugin->npp, windowScriptObject, name, args, argCount, result); } else if (name == pluginMethodIdentifiers[ID_DESTROY_NULL_STREAM]) return destroyNullStream(plugin, args, argCount, result); + else if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_NO_PAGES]) { + browser->reloadplugins(false); + return true; + } else if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_AND_PAGES]) { + browser->reloadplugins(true); + return true; + } return false; } @@ -721,6 +820,7 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass) newInstance->eventLogging = FALSE; newInstance->onStreamLoad = 0; newInstance->onStreamDestroy = 0; + newInstance->onDestroy = 0; newInstance->onURLNotify = 0; newInstance->logDestroy = FALSE; newInstance->logSetWindow = FALSE; @@ -732,6 +832,9 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass) newInstance->lastUrl = NULL; newInstance->lastHeaders = NULL; + newInstance->testDocumentOpenInDestroyStream = FALSE; + newInstance->testWindowOpen = FALSE; + return (NPObject*)newInstance; } diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h index 7437d04..157a1d2 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h @@ -38,8 +38,11 @@ typedef struct { NPBool cachedPrivateBrowsingMode; NPObject* testObject; NPStream* stream; + NPBool testDocumentOpenInDestroyStream; + NPBool testWindowOpen; char* onStreamLoad; char* onStreamDestroy; + char* onDestroy; char* onURLNotify; char* firstUrl; char* firstHeaders; @@ -55,3 +58,5 @@ extern void handleCallback(PluginObject* object, const char *url, NPReason reaso extern void notifyStream(PluginObject* object, const char *url, const char *headers); extern void testNPRuntime(NPP npp); extern void pluginLog(NPP instance, const char* format, ...); +extern bool testDocumentOpen(NPP npp); +extern bool testWindowOpen(NPP npp); diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp index 125d2e8..5883ffb 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp @@ -105,6 +105,12 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch pluginLog(instance, "src: %s", argv[i]); } else if (strcasecmp(argn[i], "cleardocumentduringnew") == 0) executeScript(obj, "document.body.innerHTML = ''"); + else if (!strcasecmp(argn[i], "ondestroy")) + obj->onDestroy = strdup(argv[i]); + else if (strcasecmp(argn[i], "testdocumentopenindestroystream") == 0) + obj->testDocumentOpenInDestroyStream = TRUE; + else if (strcasecmp(argn[i], "testwindowopen") == 0) + obj->testWindowOpen = TRUE; } #ifndef NP_NO_CARBON @@ -140,6 +146,11 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) { PluginObject* obj = static_cast<PluginObject*>(instance->pdata); if (obj) { + if (obj->onDestroy) { + executeScript(obj, obj->onDestroy); + free(obj->onDestroy); + } + if (obj->onStreamLoad) free(obj->onStreamLoad); @@ -166,6 +177,11 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window) pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height); obj->logSetWindow = false; } + + if (obj->testWindowOpen) { + testWindowOpen(instance); + obj->testWindowOpen = FALSE; + } } return NPERR_NO_ERROR; @@ -210,6 +226,11 @@ NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) if (obj->onStreamDestroy) executeScript(obj, obj->onStreamDestroy); + if (obj->testDocumentOpenInDestroyStream) { + testDocumentOpen(instance); + obj->testDocumentOpenInDestroyStream = FALSE; + } + return NPERR_NO_ERROR; } diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp index 593f2eb..df06cea 100644 --- a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp @@ -58,7 +58,11 @@ AccessibilityUIElement AccessibilityController::focusedElement() AccessibilityUIElement AccessibilityController::rootElement() { WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); - AtkObject* axObject = gtk_widget_get_accessible(GTK_WIDGET(view)); + + // The presumed, desired rootElement is the parent of the web view. + GtkWidget* webViewParent = gtk_widget_get_parent(GTK_WIDGET(view)); + AtkObject* axObject = gtk_widget_get_accessible(webViewParent); + return AccessibilityUIElement(axObject); } diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp index 9aa31a8..13d313d 100644 --- a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp @@ -161,6 +161,11 @@ JSStringRef AccessibilityUIElement::subrole() return 0; } +JSStringRef AccessibilityUIElement::roleDescription() +{ + return 0; +} + JSStringRef AccessibilityUIElement::title() { const gchar* name = atk_object_get_name(ATK_OBJECT(m_element)); @@ -181,6 +186,12 @@ JSStringRef AccessibilityUIElement::description() return JSStringCreateWithUTF8CString(description); } +JSStringRef AccessibilityUIElement::stringValue() +{ + // FIXME: implement + return JSStringCreateWithCharacters(0, 0); +} + JSStringRef AccessibilityUIElement::language() { // FIXME: implement @@ -233,6 +244,10 @@ double AccessibilityUIElement::clickPointY() return 0.f; } +JSStringRef AccessibilityUIElement::orientation() const +{ + return 0; +} double AccessibilityUIElement::intValue() { @@ -316,6 +331,34 @@ bool AccessibilityUIElement::isRequired() const return false; } +bool AccessibilityUIElement::isSelected() const +{ + // FIXME: implement + return false; +} + +int AccessibilityUIElement::hierarchicalLevel() const +{ + // FIXME: implement + return 0; +} + +bool AccessibilityUIElement::ariaIsGrabbed() const +{ + return false; +} + +JSStringRef AccessibilityUIElement::ariaDropEffects() const +{ + return 0; +} + +bool AccessibilityUIElement::isExpanded() const +{ + // FIXME: implement + return false; +} + JSStringRef AccessibilityUIElement::attributesOfColumnHeaders() { // FIXME: implement @@ -417,6 +460,11 @@ bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) return false; } +bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) +{ + return false; +} + void AccessibilityUIElement::increment() { // FIXME: implement @@ -426,3 +474,57 @@ void AccessibilityUIElement::decrement() { // FIXME: implement } + +void AccessibilityUIElement::showMenu() +{ + // FIXME: implement +} + +AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::disclosedByRow() +{ + return 0; +} + +JSStringRef AccessibilityUIElement::accessibilityValue() const +{ + // FIXME: implement + return JSStringCreateWithCharacters(0, 0); +} + +JSStringRef AccessibilityUIElement::documentEncoding() +{ + AtkRole role = atk_object_get_role(ATK_OBJECT(m_element)); + if (role != ATK_ROLE_DOCUMENT_FRAME) + return JSStringCreateWithCharacters(0, 0); + + return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element), "Encoding")); +} + +JSStringRef AccessibilityUIElement::documentURI() +{ + AtkRole role = atk_object_get_role(ATK_OBJECT(m_element)); + if (role != ATK_ROLE_DOCUMENT_FRAME) + return JSStringCreateWithCharacters(0, 0); + + return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element), "URI")); +} diff --git a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp index 4ed6e36..fd1e3c6 100644 --- a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp @@ -82,6 +82,7 @@ static GCController* gcController = 0; static WebKitWebView* webView; static GtkWidget* window; static GtkWidget* container; +static GtkWidget* webInspectorWindow; WebKitWebFrame* mainFrame = 0; WebKitWebFrame* topLoadingFrame = 0; guint waitToDumpWatchdog = 0; @@ -113,6 +114,11 @@ static bool shouldLogFrameLoadDelegates(const char* pathOrURL) return strstr(pathOrURL, "loading/"); } +static bool shouldOpenWebInspector(const char* pathOrURL) +{ + return strstr(pathOrURL, "inspector/"); +} + void dumpFrameScrollPosition(WebKitWebFrame* frame) { @@ -120,7 +126,7 @@ void dumpFrameScrollPosition(WebKitWebFrame* frame) void displayWebView() { - + gtk_widget_queue_draw(GTK_WIDGET(webView)); } static void appendString(gchar*& target, gchar* string) @@ -319,7 +325,6 @@ static void resetDefaultsToConsistentValues() "enable-offline-web-application-cache", TRUE, "enable-universal-access-from-file-uris", TRUE, "enable-scripts", TRUE, - "enable-web-sockets", TRUE, "enable-dom-paste", TRUE, "default-font-family", "Times", "monospace-font-family", "Courier", @@ -400,12 +405,8 @@ void dump() // FIXME: call displayWebView here when we support --paint - puts("#EOF"); // terminate the (possibly empty) pixels block - - fflush(stdout); - fflush(stderr); - done = true; + gtk_main_quit(); } static void setDefaultsToConsistentStateValuesForTesting() @@ -427,6 +428,14 @@ static void setDefaultsToConsistentStateValuesForTesting() g_free(databaseDirectory); } +static void sendPixelResultsEOF() +{ + puts("#EOF"); + + fflush(stdout); + fflush(stderr); +} + static void runTest(const string& testPathOrURL) { ASSERT(!testPathOrURL.empty()); @@ -455,6 +464,9 @@ static void runTest(const string& testPathOrURL) if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) gLayoutTestController->setDumpFrameLoadCallbacks(true); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + WorkQueue::shared()->clear(); WorkQueue::shared()->setFrozen(false); @@ -484,9 +496,10 @@ static void runTest(const string& testPathOrURL) g_free(url); url = NULL; - while (!done) - g_main_context_iteration(NULL, TRUE); + gtk_main(); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->closeWebInspector(); // Also check if we still have opened webViews and free them. if (gLayoutTestController->closeRemainingWindowsWhenComplete() || webViewList) { @@ -503,6 +516,9 @@ static void runTest(const string& testPathOrURL) gLayoutTestController->deref(); gLayoutTestController = 0; + + // terminate the (possibly empty) pixels block after all the state reset + sendPixelResultsEOF(); } void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*) @@ -565,7 +581,7 @@ static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void if (WorkQueue::shared()->count()) g_timeout_add(0, processWork, 0); - else + else dump(); } @@ -708,9 +724,29 @@ static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, We static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*); +static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data) +{ + gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600); + gtk_widget_show_all(webInspectorWindow); + return TRUE; +} + +static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data) +{ + gtk_widget_destroy(webInspectorWindow); + webInspectorWindow = 0; + return TRUE; +} + static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data) { - return WEBKIT_WEB_VIEW(webkit_web_view_new()); + webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + GtkWidget* webView = webkit_web_view_new(); + gtk_container_add(GTK_CONTAINER(webInspectorWindow), + webView); + + return WEBKIT_WEB_VIEW(webView); } static WebKitWebView* createWebView() @@ -738,7 +774,11 @@ static WebKitWebView* createWebView() NULL); WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); - g_signal_connect(inspector, "inspect-web-view", G_CALLBACK(webInspectorInspectWebView), 0); + g_object_connect(G_OBJECT(inspector), + "signal::inspect-web-view", webInspectorInspectWebView, 0, + "signal::show-window", webInspectorShowWindow, 0, + "signal::close-window", webInspectorCloseWindow, 0, + NULL); return view; } @@ -793,6 +833,7 @@ int main(int argc, char* argv[]) gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView)); gtk_widget_realize(GTK_WIDGET(webView)); gtk_widget_show_all(container); + gtk_widget_grab_focus(GTK_WIDGET(webView)); mainFrame = webkit_web_view_get_main_frame(webView); setDefaultsToConsistentStateValuesForTesting(); diff --git a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp index f42928c..6268b5b 100644 --- a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp @@ -200,6 +200,23 @@ static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, return JSValueMakeUndefined(context); } +static guint getStateFlags() +{ + guint state = 0; + + if (down) { + if (currentEventButton == 1) + state = GDK_BUTTON1_MASK; + else if (currentEventButton == 2) + state = GDK_BUTTON2_MASK; + else if (currentEventButton == 3) + state = GDK_BUTTON3_MASK; + } else + state = 0; + + return state; +} + static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { @@ -207,8 +224,6 @@ static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JS if (!view) return JSValueMakeUndefined(context); - down = false; - GdkEvent event; memset(&event, 0, sizeof(event)); event.type = GDK_BUTTON_RELEASE; @@ -226,6 +241,9 @@ static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JS event.button.window = GTK_WIDGET(view)->window; event.button.time = GDK_CURRENT_TIME; event.button.device = gdk_device_get_core_pointer(); + event.button.state = getStateFlags(); + + down = false; int x_root, y_root; #if GTK_CHECK_VERSION(2,17,3) @@ -286,16 +304,8 @@ static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function event.motion.x_root = x_root; event.motion.y_root = y_root; - - if (down) { - if (currentEventButton == 1) - event.motion.state = GDK_BUTTON1_MASK; - else if (currentEventButton == 2) - event.motion.state = GDK_BUTTON2_MASK; - else if (currentEventButton == 3) - event.motion.state = GDK_BUTTON3_MASK; - } else - event.motion.state = 0; + + event.motion.state = getStateFlags(); if (dragMode && down && !replayingSavedEvents) { msgQueue[endOfQueue].event = event; diff --git a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp index 631fc31..6e94c1c 100644 --- a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp @@ -49,6 +49,7 @@ extern "C" { bool webkit_web_frame_pause_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); bool webkit_web_frame_pause_transition(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); +bool webkit_web_frame_pause_svg_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); unsigned int webkit_web_frame_number_of_active_animations(WebKitWebFrame* frame); void webkit_application_cache_set_maximum_size(unsigned long long size); unsigned int webkit_worker_thread_count(void); @@ -224,7 +225,18 @@ void LayoutTestController::setMainFrameIsFirstResponder(bool flag) void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles) { - // FIXME: implement + WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); + WebKitWebSettings* settings = webkit_web_view_get_settings(webView); + g_object_set(G_OBJECT(settings), "tab-key-cycles-through-elements", cycles, NULL); +} + +void LayoutTestController::setTimelineProfilingEnabled(bool flag) +{ + WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); + ASSERT(view); + + WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); + g_object_set(G_OBJECT(inspector), "timeline-profiling-enabled", flag, NULL); } void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) @@ -305,6 +317,15 @@ void LayoutTestController::setXSSAuditorEnabled(bool flag) g_object_set(G_OBJECT(settings), "enable-xss-auditor", flag, NULL); } +void LayoutTestController::setAllowUniversalAccessFromFileURLs(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-universal-access-from-file-uris", flag, NULL); +} + void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag) { // FIXME: implement @@ -428,6 +449,16 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef prop return returnValue; } +bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId) +{ + gchar* name = JSStringCopyUTF8CString(animationId); + gchar* element = JSStringCopyUTF8CString(elementId); + bool returnValue = webkit_web_frame_pause_svg_animation(mainFrame, name, time, element); + g_free(name); + g_free(element); + return returnValue; +} + unsigned LayoutTestController::numberOfActiveAnimations() const { return webkit_web_frame_number_of_active_animations(mainFrame); @@ -493,7 +524,7 @@ void LayoutTestController::showWebInspector() WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); g_object_set(webSettings, "enable-developer-extras", TRUE, NULL); - webkit_web_inspector_inspect_coordinates(inspector, 0, 0); + webkit_web_inspector_show(inspector); } void LayoutTestController::closeWebInspector() diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm index 375dbad..948f379 100644 --- a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm @@ -40,6 +40,18 @@ #define NSAccessibilityValueDescriptionAttribute @"AXValueDescription" #endif +#ifndef NSAccessibilityOwnsAttribute +#define NSAccessibilityOwnsAttribute @"AXOwns" +#endif + +#ifndef NSAccessibilityGrabbedAttribute +#define NSAccessibilityGrabbedAttribute @"AXGrabbed" +#endif + +#ifndef NSAccessibilityDropEffectsAttribute +#define NSAccessibilityDropEffectsAttribute @"AXDropEffects" +#endif + @interface NSObject (WebKitAccessibilityArrayCategory) - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; @end @@ -232,7 +244,43 @@ AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index) if (children.size() == 1) return children[0]; - return nil; + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index) +{ + NSArray* objects = [m_element accessibilityAttributeValue:NSAccessibilityOwnsAttribute]; + if (index < [objects count]) + return [objects objectAtIndex:index]; + + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index) +{ + NSArray* objects = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute]; + if (index < [objects count]) + return [objects objectAtIndex:index]; + + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index) +{ + NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedRowsAttribute]; + if (index < [rows count]) + return [rows objectAtIndex:index]; + + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index) +{ + NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilitySelectedRowsAttribute]; + if (index < [rows count]) + return [rows objectAtIndex:index]; + + return 0; } AccessibilityUIElement AccessibilityUIElement::titleUIElement() @@ -241,7 +289,7 @@ AccessibilityUIElement AccessibilityUIElement::titleUIElement() if (accessibilityObject) return AccessibilityUIElement(accessibilityObject); - return nil; + return 0; } AccessibilityUIElement AccessibilityUIElement::parentElement() @@ -250,7 +298,16 @@ AccessibilityUIElement AccessibilityUIElement::parentElement() if (accessibilityObject) return AccessibilityUIElement(accessibilityObject); - return nil; + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::disclosedByRow() +{ + id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedByRowAttribute]; + if (accessibilityObject) + return AccessibilityUIElement(accessibilityObject); + + return 0; } JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements() @@ -293,6 +350,11 @@ bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) return [m_element accessibilityIsAttributeSettable:[NSString stringWithJSStringRef:attribute]]; } +bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) +{ + return [[m_element accessibilityAttributeNames] containsObject:[NSString stringWithJSStringRef:attribute]]; +} + JSStringRef AccessibilityUIElement::parameterizedAttributeNames() { NSArray* supportedParameterizedAttributes = [m_element accessibilityParameterizedAttributeNames]; @@ -317,6 +379,12 @@ JSStringRef AccessibilityUIElement::subrole() return concatenateAttributeAndValue(@"AXSubrole", role); } +JSStringRef AccessibilityUIElement::roleDescription() +{ + NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute], m_element); + return concatenateAttributeAndValue(@"AXRoleDescription", role); +} + JSStringRef AccessibilityUIElement::title() { NSString* title = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityTitleAttribute], m_element); @@ -329,6 +397,18 @@ JSStringRef AccessibilityUIElement::description() return concatenateAttributeAndValue(@"AXDescription", description); } +JSStringRef AccessibilityUIElement::orientation() const +{ + id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityOrientationAttribute], m_element); + return concatenateAttributeAndValue(@"AXOrientation", description); +} + +JSStringRef AccessibilityUIElement::stringValue() +{ + id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityValueAttribute], m_element); + return concatenateAttributeAndValue(@"AXValue", description); +} + JSStringRef AccessibilityUIElement::language() { id description = descriptionOfValue([m_element accessibilityAttributeValue:@"AXLanguage"], m_element); @@ -433,6 +513,55 @@ bool AccessibilityUIElement::isRequired() const return false; } +bool AccessibilityUIElement::isSelected() const +{ + id value = [m_element accessibilityAttributeValue:NSAccessibilitySelectedAttribute]; + if ([value isKindOfClass:[NSNumber class]]) + return [value boolValue]; + return false; +} + +bool AccessibilityUIElement::isExpanded() const +{ + id value = [m_element accessibilityAttributeValue:NSAccessibilityExpandedAttribute]; + if ([value isKindOfClass:[NSNumber class]]) + return [value boolValue]; + return false; +} + +int AccessibilityUIElement::hierarchicalLevel() const +{ + id value = [m_element accessibilityAttributeValue:NSAccessibilityDisclosureLevelAttribute]; + if ([value isKindOfClass:[NSNumber class]]) + return [value intValue]; + return 0; +} + +bool AccessibilityUIElement::ariaIsGrabbed() const +{ + id value = [m_element accessibilityAttributeValue:NSAccessibilityGrabbedAttribute]; + if ([value isKindOfClass:[NSNumber class]]) + return [value boolValue]; + return false; +} + +JSStringRef AccessibilityUIElement::ariaDropEffects() const +{ + id value = [m_element accessibilityAttributeValue:NSAccessibilityDropEffectsAttribute]; + if (![value isKindOfClass:[NSArray class]]) + return 0; + + NSMutableString* dropEffects = [NSMutableString string]; + NSInteger length = [value count]; + for (NSInteger k = 0; k < length; ++k) { + [dropEffects appendString:[value objectAtIndex:k]]; + if (k < length - 1) + [dropEffects appendString:@","]; + } + + return [dropEffects createJSStringRef]; +} + // parameterized attributes int AccessibilityUIElement::lineForIndex(int index) { @@ -571,3 +700,24 @@ void AccessibilityUIElement::decrement() { [m_element accessibilityPerformAction:NSAccessibilityDecrementAction]; } + +void AccessibilityUIElement::showMenu() +{ + [m_element accessibilityPerformAction:NSAccessibilityShowMenuAction]; +} + +JSStringRef AccessibilityUIElement::accessibilityValue() const +{ + // FIXME: implement + return JSStringCreateWithCharacters(0, 0); +} + +JSStringRef AccessibilityUIElement::documentEncoding() +{ + return JSStringCreateWithCharacters(0, 0); +} + +JSStringRef AccessibilityUIElement::documentURI() +{ + return JSStringCreateWithCharacters(0, 0); +} diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index 98f4f9c..4ffeac3 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -87,6 +87,10 @@ using namespace std; @interface DumpRenderTreeEvent : NSEvent @end +@interface NSURLRequest (PrivateThingsWeShouldntReallyUse) ++(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host; +@end + static void runTest(const string& testPathOrURL); // Deciding when it's OK to dump out the state is a bit tricky. All these must be true: @@ -302,7 +306,7 @@ WebView *createWebViewAndOffscreenWindow() [window orderBack:nil]; [window setAutodisplay:NO]; - [window startObservingWebView]; + [window startListeningForAcceleratedCompositingChanges]; // For reasons that are not entirely clear, the following pair of calls makes WebView handle its // dynamic scrollbars properly. Without it, every frame will always have scrollbars. @@ -596,9 +600,17 @@ void dumpRenderTree(int argc, const char *argv[]) mainFrame = [webView mainFrame]; [[NSURLCache sharedURLCache] removeAllCachedResponses]; - [WebCache empty]; - + + // <http://webkit.org/b/31200> In order to prevent extra frame load delegate logging being generated if the first test to use SSL + // is set to log frame load delegate calls we ignore SSL certificate errors on localhost and 127.0.0.1. +#if BUILDING_ON_TIGER + // Initialize internal NSURLRequest data for setAllowsAnyHTTPSCertificate:forHost: to work properly. + [[[[NSURLRequest alloc] init] autorelease] HTTPMethod]; +#endif + [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"localhost"]; + [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"127.0.0.1"]; + // <rdar://problem/5222911> testStringByEvaluatingJavaScriptFromString(); @@ -1090,6 +1102,11 @@ static bool shouldLogHistoryDelegates(const char* pathOrURL) return strstr(pathOrURL, "globalhistory/"); } +static bool shouldOpenWebInspector(const char* pathOrURL) +{ + return strstr(pathOrURL, "inspector/"); +} + static void resetWebViewToConsistentStateBeforeTesting() { WebView *webView = [mainFrame webView]; @@ -1105,6 +1122,7 @@ static void resetWebViewToConsistentStateBeforeTesting() [webView _clearMainFrameName]; [[webView undoManager] removeAllActions]; [WebView _removeAllUserContentFromGroup:[webView groupName]]; + [[webView window] setAutodisplay:NO]; resetDefaultsToConsistentValues(); @@ -1166,7 +1184,10 @@ static void runTest(const string& testPathOrURL) [[mainFrame webView] setHistoryDelegate:historyDelegate]; else [[mainFrame webView] setHistoryDelegate:nil]; - + + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + if ([WebHistory optionalSharedHistory]) [WebHistory setOptionalSharedHistory:nil]; lastMousePosition = NSZeroPoint; @@ -1190,6 +1211,7 @@ static void runTest(const string& testPathOrURL) [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]; [pool release]; } + pool = [[NSAutoreleasePool alloc] init]; [EventSendingController clearSavedEvents]; [[mainFrame webView] setSelectedDOMRange:nil affinity:NSSelectionAffinityDownstream]; @@ -1214,11 +1236,14 @@ static void runTest(const string& testPathOrURL) } } + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->closeWebInspector(); + resetWebViewToConsistentStateBeforeTesting(); [mainFrame loadHTMLString:@"<html></html>" baseURL:[NSURL URLWithString:@"about:blank"]]; [mainFrame stopLoading]; - + [pool release]; // We should only have our main window left open when we're done diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h index 72d5db1..fe1ac00 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeMac.h @@ -42,6 +42,7 @@ @class NavigationController; @class PolicyDelegate; @class WebFrame; +@class WebScriptWorld; @class WebView; typedef const struct __CFString* CFStringRef; @@ -62,4 +63,6 @@ extern CFRunLoopTimerRef waitToDumpWatchdog; WebView* createWebViewAndOffscreenWindow(); void setPersistentUserStyleSheetLocation(CFStringRef); +unsigned worldIDForWorld(WebScriptWorld *); + #endif // DumpRenderTreeMac_h diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h index b6bdcb8..a229d20 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.h @@ -34,7 +34,6 @@ @interface DumpRenderTreeWindow : NSWindow { - BOOL observingWebView; } // I'm not sure why we can't just use [NSApp windows] @@ -42,7 +41,6 @@ - (WebView *)webView; -- (void)startObservingWebView; -- (void)stopObservingWebView; +- (void)startListeningForAcceleratedCompositingChanges; @end diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm index aa5b117..8845ef0 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTreeWindow.mm @@ -67,7 +67,7 @@ static CFArrayCallBacks NonRetainingArrayCallbacks = { - (void)close { - [self stopObservingWebView]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; CFRange arrayRange = CFRangeMake(0, CFArrayGetCount(openWindowsRef)); CFIndex i = CFArrayGetFirstIndexOfValue(openWindowsRef, arrayRange, self); @@ -99,32 +99,20 @@ static CFArrayCallBacks NonRetainingArrayCallbacks = { return nil; } -- (void)startObservingWebView +- (void)startListeningForAcceleratedCompositingChanges { - [self stopObservingWebView]; - [[self webView] addObserver:self forKeyPath:@"_isUsingAcceleratedCompositing" options:0 context:0]; - observingWebView = YES; + [[self webView] _setPostsAcceleratedCompositingNotifications:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(webViewStartedAcceleratedCompositing:) + name:_WebViewDidStartAcceleratedCompositingNotification object:nil]; } -- (void)stopObservingWebView +- (void)webViewStartedAcceleratedCompositing:(NSNotification *)notification { - if (!observingWebView) - return; - [[self webView] removeObserver:self forKeyPath:@"_isUsingAcceleratedCompositing"]; - observingWebView = NO; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([keyPath isEqualToString:@"_isUsingAcceleratedCompositing"]) { - // When using accelerated compositing, the window needs to be autodisplay for AppKit/CA to - // start accelerated animations correctly. - BOOL isAccelerated = [[self webView] _isUsingAcceleratedCompositing]; - [self setAutodisplay:isAccelerated]; - return; - } - - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + // If the WebView has gone into compositing mode, turn on window autodisplay. This is necessary for CA + // to update layers and start animations. + // We only ever turn autodisplay on here, because we turn it off before every test. + if ([[self webView] _isUsingAcceleratedCompositing]) + [self setAutodisplay:YES]; } @end diff --git a/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm index 2838d2e..963eae7 100644 --- a/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/FrameLoadDelegate.mm @@ -48,13 +48,10 @@ #import <WebKit/WebHTMLViewPrivate.h> #import <WebKit/WebKit.h> #import <WebKit/WebNSURLExtras.h> +#import <WebKit/WebScriptWorld.h> #import <WebKit/WebSecurityOriginPrivate.h> #import <wtf/Assertions.h> -@interface NSURLRequest (PrivateThingsWeShouldntReallyUse) -+(void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host; -@end - @interface NSURL (DRTExtras) - (NSString *)_drt_descriptionSuitableForTestResult; @end @@ -182,13 +179,15 @@ { if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { NSString *string = [NSString stringWithFormat:@"%@ - didFailProvisionalLoadWithError", [frame _drt_descriptionSuitableForTestResult]]; - printf ("%s\n", [string UTF8String]); + printf("%s\n", [string UTF8String]); } if ([error domain] == NSURLErrorDomain && ([error code] == NSURLErrorServerCertificateHasUnknownRoot || [error code] == NSURLErrorServerCertificateUntrusted)) { - NSURL *failedURL = [[error userInfo] objectForKey:@"NSErrorFailingURLKey"]; - [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[failedURL _web_hostString]]; - [frame loadRequest:[[[[frame provisionalDataSource] request] mutableCopy] autorelease]]; + // <http://webkit.org/b/31200> In order to prevent extra frame load delegate logging being generated if the first test to use SSL + // is set to log frame load delegate calls we ignore SSL certificate errors on localhost and 127.0.0.1 from within dumpRenderTree. + // Those are the only hosts that we use SSL with at present. If we hit this code path then we've found another host that we need + // to apply the workaround to. + ASSERT_NOT_REACHED(); return; } @@ -238,11 +237,8 @@ ASSERT_NOT_REACHED(); } -- (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)obj forFrame:(WebFrame *)frame +- (void)didClearWindowObjectInStandardWorldForFrame:(WebFrame *)frame { - ASSERT(obj == [frame windowObject]); - ASSERT([obj JSObject] == JSContextGetGlobalObject([frame globalContext])); - // Make New-Style LayoutTestController JSContextRef context = [frame globalContext]; JSObjectRef globalObject = JSContextGetGlobalObject(context); @@ -260,7 +256,9 @@ // Make Old-Style controllers - AppleScriptController *asc = [[AppleScriptController alloc] initWithWebView:sender]; + WebView *webView = [frame webView]; + WebScriptObject *obj = [frame windowObject]; + AppleScriptController *asc = [[AppleScriptController alloc] initWithWebView:webView]; [obj setValue:asc forKey:@"appleScriptController"]; [asc release]; @@ -284,11 +282,32 @@ [obj setValue:[PlainTextController sharedPlainTextController] forKey:@"plainText"]; - TextInputController *tic = [[TextInputController alloc] initWithWebView:sender]; + TextInputController *tic = [[TextInputController alloc] initWithWebView:webView]; [obj setValue:tic forKey:@"textInputController"]; [tic release]; } +- (void)didClearWindowObjectForFrame:(WebFrame *)frame inIsolatedWorld:(WebScriptWorld *)world +{ + JSGlobalContextRef ctx = [frame _globalContextForScriptWorld:world]; + if (!ctx) + return; + + JSObjectRef globalObject = JSContextGetGlobalObject(ctx); + if (!globalObject) + return; + + JSObjectSetProperty(ctx, globalObject, JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString("__worldID")).get(), JSValueMakeNumber(ctx, worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0); +} + +- (void)webView:(WebView *)sender didClearWindowObjectForFrame:(WebFrame *)frame inScriptWorld:(WebScriptWorld *)world +{ + if (world == [WebScriptWorld standardWorld]) + [self didClearWindowObjectInStandardWorldForFrame:frame]; + else + [self didClearWindowObjectForFrame:frame inIsolatedWorld:world]; +} + - (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm index 4d6a609..69fe19f 100644 --- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -43,25 +43,27 @@ #import <WebKit/WebApplicationCache.h> #import <WebKit/WebBackForwardList.h> #import <WebKit/WebCoreStatistics.h> -#import <WebKit/WebDatabaseManagerPrivate.h> #import <WebKit/WebDataSource.h> +#import <WebKit/WebDatabaseManagerPrivate.h> #import <WebKit/WebFrame.h> #import <WebKit/WebFrameViewPrivate.h> -#import <WebKit/WebIconDatabasePrivate.h> +#import <WebKit/WebGeolocationMockPrivate.h> #import <WebKit/WebHTMLRepresentation.h> #import <WebKit/WebHTMLViewPrivate.h> #import <WebKit/WebHistory.h> #import <WebKit/WebHistoryPrivate.h> +#import <WebKit/WebIconDatabasePrivate.h> #import <WebKit/WebInspectorPrivate.h> -#import <WebKit/WebGeolocationMockPrivate.h> #import <WebKit/WebNSURLExtras.h> #import <WebKit/WebPreferences.h> #import <WebKit/WebPreferencesPrivate.h> +#import <WebKit/WebScriptWorld.h> #import <WebKit/WebSecurityOriginPrivate.h> #import <WebKit/WebTypesInternal.h> #import <WebKit/WebView.h> #import <WebKit/WebViewPrivate.h> #import <WebKit/WebWorkersPrivate.h> +#import <wtf/HashMap.h> #import <wtf/RetainPtr.h> @interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem> @@ -299,6 +301,11 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) [[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled]; } +void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) +{ + [[[mainFrame webView] preferences] setAllowUniversalAccessFromFileURLs:enabled]; +} + void LayoutTestController::setPopupBlockingEnabled(bool popupBlockingEnabled) { [[[mainFrame webView] preferences] setJavaScriptCanOpenWindowsAutomatically:!popupBlockingEnabled]; @@ -309,6 +316,11 @@ void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles) [[mainFrame webView] setTabKeyCyclesThroughElements:cycles]; } +void LayoutTestController::setTimelineProfilingEnabled(bool enabled) +{ + [[[mainFrame webView] inspector] setTimelineProfilingEnabled:enabled]; +} + void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) { [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:flag]; @@ -474,6 +486,16 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef prop return [mainFrame _pauseTransitionOfProperty:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time]; } +bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId) +{ + RetainPtr<CFStringRef> animationIDCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationId)); + NSString *animationIDNS = (NSString *)animationIDCF.get(); + RetainPtr<CFStringRef> elementIDCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId)); + NSString *elementIDNS = (NSString *)elementIDCF.get(); + + return [mainFrame _pauseSVGAnimation:elementIDNS onSMILNode:[[mainFrame DOMDocument] getElementById:animationIDNS] atTime:time]; +} + unsigned LayoutTestController::numberOfActiveAnimations() const { return [mainFrame _numberOfActiveAnimations]; @@ -501,14 +523,14 @@ void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) { RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source)); NSString *sourceNS = (NSString *)sourceCF.get(); - [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" worldID:1 source:sourceNS url:nil whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)]; + [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)]; } void LayoutTestController::addUserStyleSheet(JSStringRef source) { RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source)); NSString *sourceNS = (NSString *)sourceCF.get(); - [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" worldID:1 source:sourceNS url:nil whitelist:nil blacklist:nil]; + [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil]; } void LayoutTestController::showWebInspector() @@ -530,9 +552,40 @@ void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef scrip [[[mainFrame webView] inspector] evaluateInFrontend:nil callId:callId script:scriptNS]; } +typedef HashMap<unsigned, RetainPtr<WebScriptWorld> > WorldMap; +static WorldMap& worldMap() +{ + static WorldMap& map = *new WorldMap; + return map; +} + +unsigned worldIDForWorld(WebScriptWorld *world) +{ + WorldMap::const_iterator end = worldMap().end(); + for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) { + if (it->second == world) + return it->first; + } + + return 0; +} + void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) { RetainPtr<CFStringRef> scriptCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, script)); NSString *scriptNS = (NSString *)scriptCF.get(); - [mainFrame _stringByEvaluatingJavaScriptInIsolatedWorld:worldID WithGlobalObject:globalObject FromString:scriptNS]; + + // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world + // that is created once and cached forever. + WebScriptWorld *world; + if (!worldID) + world = [WebScriptWorld world]; + else { + RetainPtr<WebScriptWorld>& worldSlot = worldMap().add(worldID, 0).first->second; + if (!worldSlot) + worldSlot.adoptNS([[WebScriptWorld alloc] init]); + world = worldSlot.get(); + } + + [mainFrame _stringByEvaluatingJavaScriptFromString:scriptNS withGlobalObject:globalObject inScriptWorld:world]; } diff --git a/WebKitTools/DumpRenderTree/mac/UIDelegate.mm b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm index 393899e..81c03d2 100644 --- a/WebKitTools/DumpRenderTree/mac/UIDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm @@ -34,9 +34,9 @@ #import "EventSendingController.h" #import "LayoutTestController.h" #import <WebKit/WebFramePrivate.h> -#import <WebKit/WebGeolocationPrivate.h> #import <WebKit/WebHTMLViewPrivate.h> #import <WebKit/WebSecurityOriginPrivate.h> +#import <WebKit/WebUIDelegatePrivate.h> #import <WebKit/WebView.h> #import <wtf/Assertions.h> @@ -151,10 +151,14 @@ DumpRenderTreeDraggingInfo *draggingInfo = nil; printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", [text UTF8String]); } -- (void)webView:(WebView *)sender frame:(WebFrame *)frame requestGeolocationPermission:(WebGeolocation *)geolocation securityOrigin:(WebSecurityOrigin *)origin +- (void)webView:(WebView *)webView decidePolicyForGeolocationRequestFromOrigin:(WebSecurityOrigin *)origin frame:(WebFrame *)frame listener:(id<WebGeolocationPolicyListener>)listener { - if (gLayoutTestController->isGeolocationPermissionSet()) - [geolocation setIsAllowed:gLayoutTestController->geolocationPermission()]; + if (gLayoutTestController->isGeolocationPermissionSet()) { + if (gLayoutTestController->geolocationPermission()) + [listener allow]; + else + [listener deny]; + } } - (BOOL)webView:(WebView *)sender shouldHaltPlugin:(DOMNode *)pluginNode diff --git a/WebKitTools/DumpRenderTree/pthreads/JavaScriptThreadingPthreads.cpp b/WebKitTools/DumpRenderTree/pthreads/JavaScriptThreadingPthreads.cpp index 3ac257d..5a48b27 100644 --- a/WebKitTools/DumpRenderTree/pthreads/JavaScriptThreadingPthreads.cpp +++ b/WebKitTools/DumpRenderTree/pthreads/JavaScriptThreadingPthreads.cpp @@ -28,6 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include "JavaScriptThreading.h" #include <CoreFoundation/CoreFoundation.h> diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp index 9faa37f..6d466bf 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp @@ -29,6 +29,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + #include "DumpRenderTree.h" #include "EventSenderQt.h" #include "LayoutTestControllerQt.h" @@ -41,25 +43,29 @@ #include <QCryptographicHash> #include <QDir> #include <QFile> -#include <QTimer> -#include <QBoxLayout> -#include <QScrollArea> #include <QApplication> #include <QUrl> +#include <QFileInfo> #include <QFocusEvent> #include <QFontDatabase> +#include <QNetworkAccessManager> +#include <QNetworkReply> #include <QNetworkRequest> +#include <QUndoStack> -#include <qwebpage.h> -#include <qwebframe.h> -#include <qwebview.h> #include <qwebsettings.h> #include <qwebsecurityorigin.h> +#ifndef QT_NO_UITOOLS +#include <QtUiTools/QUiLoader> +#endif + #ifdef Q_WS_X11 #include <fontconfig/fontconfig.h> #endif +#include <limits.h> + #include <unistd.h> #include <qdebug.h> @@ -69,6 +75,7 @@ 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); namespace WebCore { @@ -76,39 +83,39 @@ namespace WebCore { const unsigned int maxViewWidth = 800; const unsigned int maxViewHeight = 600; -class WebPage : public QWebPage { - Q_OBJECT -public: - WebPage(QWidget *parent, DumpRenderTree *drt); - - QWebPage *createWindow(QWebPage::WebWindowType); - - void javaScriptAlert(QWebFrame *frame, const QString& message); - void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID); - bool javaScriptConfirm(QWebFrame *frame, const QString& msg); - bool javaScriptPrompt(QWebFrame *frame, const QString& msg, const QString& defaultValue, QString* result); - - void resetSettings(); - -public slots: - bool shouldInterruptJavaScript() { return false; } +NetworkAccessManager::NetworkAccessManager(QObject* parent) + : QNetworkAccessManager(parent) +{ +#ifndef QT_NO_SSL + connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), + this, SLOT(sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&))); +#endif +} -protected: - bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type); +#ifndef QT_NO_SSL +void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QList<QSslError>& errors) +{ + if (reply->url().host() == "127.0.0.1" || reply->url().host() == "localhost") { + bool ignore = true; + + // Accept any HTTPS certificate. + foreach (const QSslError& error, errors) { + if (error.error() < QSslError::UnableToGetIssuerCertificate || error.error() > QSslError::HostNameMismatch) { + ignore = false; + break; + } + } -private slots: - void setViewGeometry(const QRect &r) - { - QWidget *v = view(); - if (v) - v->setGeometry(r); + if (ignore) + reply->ignoreSslErrors(); } -private: - DumpRenderTree *m_drt; -}; +} +#endif -WebPage::WebPage(QWidget *parent, DumpRenderTree *drt) - : QWebPage(parent), m_drt(drt) +WebPage::WebPage(QObject* parent, DumpRenderTree* drt) + : QWebPage(parent) + , m_webInspector(0) + , m_drt(drt) { QWebSettings* globalSettings = QWebSettings::globalSettings(); @@ -129,21 +136,37 @@ WebPage::WebPage(QWidget *parent, DumpRenderTree *drt) connect(this, SIGNAL(geometryChangeRequested(const QRect &)), this, SLOT(setViewGeometry(const QRect & ))); + setNetworkAccessManager(new NetworkAccessManager(this)); setPluginFactory(new TestPlugin(this)); } +WebPage::~WebPage() +{ + delete m_webInspector; +} + +QWebInspector* WebPage::webInspector() +{ + if (!m_webInspector) { + m_webInspector = new QWebInspector; + m_webInspector->setPage(this); + } + return m_webInspector; +} + void WebPage::resetSettings() { // After each layout test, reset the settings that may have been changed by // layoutTestController.overridePreference() or similar. settings()->resetFontSize(QWebSettings::DefaultFontSize); - settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows); settings()->resetAttribute(QWebSettings::JavascriptEnabled); settings()->resetAttribute(QWebSettings::PrivateBrowsingEnabled); settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain); settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled); + settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls); + QWebSettings::setMaximumPagesInCache(0); // reset to default } QWebPage *WebPage::createWindow(QWebPage::WebWindowType) @@ -153,22 +176,52 @@ QWebPage *WebPage::createWindow(QWebPage::WebWindowType) void WebPage::javaScriptAlert(QWebFrame*, const QString& message) { + if (!isTextOutputEnabled()) + return; + fprintf(stdout, "ALERT: %s\n", message.toUtf8().constData()); } +static QString urlSuitableForTestResult(const QString& url) +{ + if (url.isEmpty() || !url.startsWith(QLatin1String("file://"))) + return url; + + return QFileInfo(url).fileName(); +} + void WebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString&) { - fprintf (stdout, "CONSOLE MESSAGE: line %d: %s\n", lineNumber, message.toUtf8().constData()); + if (!isTextOutputEnabled()) + return; + + QString newMessage; + if (!message.isEmpty()) { + newMessage = message; + + size_t fileProtocol = newMessage.indexOf(QLatin1String("file://")); + if (fileProtocol != -1) { + newMessage = newMessage.left(fileProtocol) + urlSuitableForTestResult(newMessage.mid(fileProtocol)); + } + } + + fprintf (stdout, "CONSOLE MESSAGE: line %d: %s\n", lineNumber, newMessage.toUtf8().constData()); } bool WebPage::javaScriptConfirm(QWebFrame*, const QString& msg) { + if (!isTextOutputEnabled()) + return true; + fprintf(stdout, "CONFIRM: %s\n", msg.toUtf8().constData()); return true; } bool WebPage::javaScriptPrompt(QWebFrame*, const QString& msg, const QString& defaultValue, QString* result) { + if (!isTextOutputEnabled()) + return true; + fprintf(stdout, "PROMPT: %s, default text: %s\n", msg.toUtf8().constData(), defaultValue.toUtf8().constData()); *result = defaultValue; return true; @@ -203,35 +256,83 @@ bool WebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& r typeDescription = "illegal value"; } - fprintf(stdout, "Policy delegate: attempt to load %s with navigation type '%s'\n", - url.toUtf8().constData(), typeDescription.toUtf8().constData()); + if (isTextOutputEnabled()) + fprintf(stdout, "Policy delegate: attempt to load %s with navigation type '%s'\n", + url.toUtf8().constData(), typeDescription.toUtf8().constData()); + m_drt->layoutTestController()->notifyDone(); } return QWebPage::acceptNavigationRequest(frame, request, type); } +bool WebPage::supportsExtension(QWebPage::Extension extension) const +{ + if (extension == QWebPage::ErrorPageExtension) + return m_drt->layoutTestController()->shouldHandleErrorPages(); + + return false; +} + +bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) +{ + const QWebPage::ErrorPageExtensionOption* info = static_cast<const QWebPage::ErrorPageExtensionOption*>(option); + + // Lets handle error pages for the main frame for now. + if (info->frame != mainFrame()) + return false; + + QWebPage::ErrorPageExtensionReturn* errorPage = static_cast<QWebPage::ErrorPageExtensionReturn*>(output); + + errorPage->content = QString("data:text/html,<body/>").toUtf8(); + + return true; +} + +QObject* WebPage::createPlugin(const QString& classId, const QUrl& url, const QStringList& paramNames, const QStringList& paramValues) +{ + Q_UNUSED(url); + Q_UNUSED(paramNames); + Q_UNUSED(paramValues); +#ifndef QT_NO_UITOOLS + QUiLoader loader; + return loader.createWidget(classId, view()); +#else + Q_UNUSED(classId); + return 0; +#endif +} + DumpRenderTree::DumpRenderTree() : m_dumpPixels(false) , m_stdin(0) , m_notifier(0) + , m_enableTextOutput(false) { qt_drt_overwritePluginDirectories(); QWebSettings::enablePersistentStorage(); + // create our primary testing page/view. + m_mainView = new QWebView(0); + m_mainView->resize(QSize(maxViewWidth, maxViewHeight)); + m_page = new WebPage(m_mainView, this); + m_mainView->setPage(m_page); + + // create out controllers. This has to be done before connectFrame, + // as it exports there to the JavaScript DOM window. m_controller = new LayoutTestController(this); connect(m_controller, SIGNAL(done()), this, SLOT(dump())); + m_eventSender = new EventSender(m_page); + m_textInputController = new TextInputController(m_page); + m_gcController = new GCController(m_page); - QWebView *view = new QWebView(0); - view->resize(QSize(maxViewWidth, maxViewHeight)); - m_page = new WebPage(view, this); - view->setPage(m_page); - connect(m_page, SIGNAL(frameCreated(QWebFrame *)), this, SLOT(connectFrame(QWebFrame *))); + // now connect our different signals + connect(m_page, SIGNAL(frameCreated(QWebFrame *)), + this, SLOT(connectFrame(QWebFrame *))); connectFrame(m_page->mainFrame()); - connect(m_page->mainFrame(), SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool))); + connect(m_page, SIGNAL(loadFinished(bool)), + m_controller, SLOT(maybeDump(bool))); - m_page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); - m_page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)), SLOT(titleChanged(const QString&))); connect(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)), @@ -239,20 +340,15 @@ DumpRenderTree::DumpRenderTree() connect(m_page, SIGNAL(statusBarMessage(const QString&)), this, SLOT(statusBarMessage(const QString&))); - m_eventSender = new EventSender(m_page); - m_textInputController = new TextInputController(m_page); - m_gcController = new GCController(m_page); - QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection); qt_drt_run(true); QFocusEvent event(QEvent::FocusIn, Qt::ActiveWindowFocusReason); - QApplication::sendEvent(view, &event); + QApplication::sendEvent(m_mainView, &event); } DumpRenderTree::~DumpRenderTree() { - delete m_page; - + delete m_mainView; delete m_stdin; delete m_notifier; } @@ -270,29 +366,48 @@ void DumpRenderTree::open() } } -void DumpRenderTree::resetToConsistentStateBeforeTesting() +static void clearHistory(QWebPage* page) { - closeRemainingWindows(); + // QWebHistory::clear() leaves current page, so remove it as well by setting + // max item count to 0, and then setting it back to it's original value. + + QWebHistory* history = page->history(); + int itemCount = history->maximumItemCount(); + + history->clear(); + history->setMaximumItemCount(0); + history->setMaximumItemCount(itemCount); +} - // Reset so that any current loads are stopped +void DumpRenderTree::resetToConsistentStateBeforeTesting() +{ + // reset so that any current loads are stopped + // NOTE: that this has to be done before the layoutTestController is + // reset or we get timeouts for some tests. m_page->blockSignals(true); m_page->triggerAction(QWebPage::Stop); m_page->blockSignals(false); - m_page->mainFrame()->setZoomFactor(1.0); + // reset the layoutTestController at this point, so that we under no + // circumstance dump (stop the waitUntilDone timer) during the reset + // of the DRT. + m_controller->reset(); + + closeRemainingWindows(); - static_cast<WebPage*>(m_page)->resetSettings(); + m_page->resetSettings(); + m_page->undoStack()->clear(); + m_page->mainFrame()->setZoomFactor(1.0); + clearHistory(m_page); qt_drt_clearFrameName(m_page->mainFrame()); WorkQueue::shared()->clear(); - // Causes timeout, why? - //WorkQueue::shared()->setFrozen(false); + WorkQueue::shared()->setFrozen(false); - m_controller->reset(); qt_drt_resetOriginAccessWhiteLists(); - QLocale qlocale; - QLocale::setDefault(qlocale); + QLocale::setDefault(QLocale::c()); + setlocale(LC_ALL, ""); } void DumpRenderTree::open(const QUrl& aurl) @@ -316,7 +431,7 @@ void DumpRenderTree::open(const QUrl& aurl) bool isW3CTest = url.toString().contains("svg/W3C-SVG-1.1"); int width = isW3CTest ? 480 : maxViewWidth; int height = isW3CTest ? 360 : maxViewHeight; - m_page->view()->resize(QSize(width, height)); + m_mainView->resize(QSize(width, height)); m_page->setPreferredContentsSize(QSize()); m_page->setViewportSize(QSize(width, height)); @@ -329,6 +444,7 @@ void DumpRenderTree::open(const QUrl& aurl) #endif qt_dump_frame_loader(url.toString().contains("loading/")); + setTextOutputEnabled(true); m_page->mainFrame()->load(url); } @@ -359,7 +475,7 @@ void DumpRenderTree::setDumpPixels(bool dump) void DumpRenderTree::closeRemainingWindows() { - foreach(QWidget *widget, windows) + foreach (QObject* widget, windows) delete widget; windows.clear(); } @@ -377,7 +493,7 @@ void DumpRenderTree::initJSObjects() QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame) { - if (!frame) + if (!frame || !qt_drt_hasDocumentElement(frame)) return QString(); QString result; @@ -388,7 +504,8 @@ QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame) result.append(QLatin1String("'\n--------\n")); } - result.append(frame->toPlainText()); + QString innerText = frame->toPlainText(); + result.append(innerText); result.append(QLatin1String("\n")); if (m_controller->shouldDumpChildrenAsText()) { @@ -400,11 +517,69 @@ QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame) return result; } +static QString dumpHistoryItem(const QWebHistoryItem& item, int indent, bool current) +{ + QString result; + + int start = 0; + if (current) { + result.append(QLatin1String("curr->")); + start = 6; + } + for (int i = start; i < indent; i++) + result.append(' '); + + QString url = item.url().toString(); + if (url.contains("file://")) { + static QString layoutTestsString("/LayoutTests/"); + static QString fileTestString("(file test):"); + + QString res = url.mid(url.indexOf(layoutTestsString) + layoutTestsString.length()); + if (res.isEmpty()) + return result; + + result.append(fileTestString); + result.append(res); + } else { + result.append(url); + } + + // FIXME: Wrong, need (private?) API for determining this. + result.append(QLatin1String(" **nav target**")); + result.append(QLatin1String("\n")); + + return result; +} + QString DumpRenderTree::dumpBackForwardList() { + QWebHistory* history = webPage()->history(); + QString result; result.append(QLatin1String("\n============== Back Forward List ==============\n")); - result.append(QLatin1String("FIXME: Unimplemented!\n")); + + // FORMAT: + // " (file test):fast/loader/resources/click-fragment-link.html **nav target**" + // "curr-> (file test):fast/loader/resources/click-fragment-link.html#testfragment **nav target**" + + int maxItems = history->maximumItemCount(); + + foreach (const QWebHistoryItem item, history->backItems(maxItems)) { + if (!item.isValid()) + continue; + result.append(dumpHistoryItem(item, 8, false)); + } + + QWebHistoryItem item = history->currentItem(); + if (item.isValid()) + result.append(dumpHistoryItem(item, 8, true)); + + foreach (const QWebHistoryItem item, history->forwardItems(maxItems)) { + if (!item.isValid()) + continue; + result.append(dumpHistoryItem(item, 8, false)); + } + result.append(QLatin1String("===============================================\n")); return result; } @@ -551,25 +726,28 @@ QWebPage *DumpRenderTree::createWindow() { if (!m_controller->canOpenWindows()) return 0; - QWidget *container = new QWidget(0); - container->resize(0, 0); - container->move(-1, -1); - container->hide(); - QWebPage *page = new WebPage(container, this); - connectFrame(page->mainFrame()); - connect(m_page, SIGNAL(frameCreated(QWebFrame *)), this, SLOT(connectFrame(QWebFrame *))); + + // Create a dummy container object to track the page in DRT. + // QObject is used instead of QWidget to prevent DRT from + // showing the main view when deleting the container. + + QObject* container = new QObject(m_mainView); + // create a QWebPage we want to return + QWebPage* page = static_cast<QWebPage*>(new WebPage(container, this)); + // gets cleaned up in closeRemainingWindows() windows.append(container); + + // connect the needed signals to the page + connect(page, SIGNAL(frameCreated(QWebFrame*)), this, SLOT(connectFrame(QWebFrame*))); + connectFrame(page->mainFrame()); + connect(page, SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool))); return page; } int DumpRenderTree::windowCount() const { - int count = 0; - foreach(QWidget *w, windows) { - if (w->children().count()) - ++count; - } - return count + 1; +// include the main view in the count + return windows.count() + 1; } #if defined(Q_WS_X11) @@ -613,5 +791,3 @@ void DumpRenderTree::initializeFonts() #endif } - -#include "DumpRenderTree.moc" diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h index c5b4801..ab229fe 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h @@ -31,15 +31,25 @@ #define DUMPRENDERTREE_H #include <QList> +#include <QNetworkAccessManager> #include <QObject> #include <QTextStream> #include <QSocketNotifier> +#ifndef QT_NO_SSL +#include <QSslError> +#endif + +#include <qwebframe.h> +#include <qwebinspector.h> +#include <qwebpage.h> +#include <qwebview.h> + QT_BEGIN_NAMESPACE class QUrl; class QFile; QT_END_NAMESPACE -class QWebPage; + class QWebFrame; class LayoutTestController; @@ -49,6 +59,8 @@ class GCController; namespace WebCore { +class WebPage; + class DumpRenderTree : public QObject { Q_OBJECT @@ -62,6 +74,9 @@ public: // Initialize in single-file mode. void open(const QUrl& url); + void setTextOutputEnabled(bool enable) { m_enableTextOutput = enable; } + bool isTextOutputEnabled() { return m_enableTextOutput; } + void setDumpPixels(bool); void closeRemainingWindows(); @@ -74,7 +89,7 @@ public: QWebPage *createWindow(); int windowCount() const; - QWebPage *webPage() const { return m_page; } + WebPage *webPage() const { return m_page; } #if defined(Q_WS_X11) @@ -101,7 +116,8 @@ private: bool m_dumpPixels; QString m_expectedHash; - QWebPage *m_page; + WebPage *m_page; + QWebView* m_mainView; EventSender *m_eventSender; TextInputController *m_textInputController; @@ -110,7 +126,59 @@ private: QFile *m_stdin; QSocketNotifier* m_notifier; - QList<QWidget *> windows; + QList<QObject*> windows; + bool m_enableTextOutput; +}; + +class NetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT +public: + NetworkAccessManager(QObject* parent); + +private slots: +#ifndef QT_NO_SSL + void sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&); +#endif +}; + +class WebPage : public QWebPage { + Q_OBJECT +public: + WebPage(QObject* parent, DumpRenderTree*); + virtual ~WebPage(); + QWebInspector* webInspector(); + + QWebPage *createWindow(QWebPage::WebWindowType); + + void javaScriptAlert(QWebFrame *frame, const QString& message); + void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID); + bool javaScriptConfirm(QWebFrame *frame, const QString& msg); + bool javaScriptPrompt(QWebFrame *frame, const QString& msg, const QString& defaultValue, QString* result); + + void resetSettings(); + + virtual bool supportsExtension(QWebPage::Extension extension) const; + virtual bool extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output); + + QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&); + +public slots: + bool shouldInterruptJavaScript() { return false; } + +protected: + bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type); + bool isTextOutputEnabled() { return m_drt->isTextOutputEnabled(); } + +private slots: + void setViewGeometry(const QRect &r) + { + QWidget *v = view(); + if (v) + v->setGeometry(r); + } +private: + QWebInspector* m_webInspector; + DumpRenderTree *m_drt; }; } diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro index 571313c..81e929b 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro @@ -1,9 +1,20 @@ TARGET = DumpRenderTree CONFIG -= app_bundle +CONFIG += uitools + +mac:!static:contains(QT_CONFIG, qt_framework):!CONFIG(webkit_no_framework) { + CONFIG -= debug + CONFIG += release +} + +BASEDIR = $$PWD/../ include(../../../WebKit.pri) INCLUDEPATH += /usr/include/freetype2 +INCLUDEPATH += ../../.. INCLUDEPATH += ../../../JavaScriptCore +INCLUDEPATH += ../../../JavaScriptCore/ForwardingHeaders +INCLUDEPATH += $$BASEDIR DESTDIR = ../../../bin CONFIG += link_pkgconfig @@ -12,7 +23,7 @@ PKGCONFIG += fontconfig QT = core gui network macx: QT += xml -HEADERS = WorkQueue.h \ +HEADERS = $$BASEDIR/WorkQueue.h \ WorkQueueItem.h \ DumpRenderTree.h \ EventSenderQt.h \ @@ -20,7 +31,7 @@ HEADERS = WorkQueue.h \ LayoutTestControllerQt.h \ jsobjects.h \ testplugin.h -SOURCES = WorkQueue.cpp \ +SOURCES = $$BASEDIR/WorkQueue.cpp \ DumpRenderTree.cpp \ EventSenderQt.cpp \ TextInputControllerQt.cpp \ @@ -34,8 +45,4 @@ unix:!mac { QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR } -lessThan(QT_MINOR_VERSION, 4) { - DEFINES += QT_BEGIN_NAMESPACE="" QT_END_NAMESPACE="" -} - DEFINES+=USE_SYSTEM_MALLOC diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp index bbefed9..a0da273 100644 --- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp @@ -30,6 +30,15 @@ #include "EventSenderQt.h" //#include <QtDebug> + +#define KEYCODE_DEL 127 +#define KEYCODE_BACKSPACE 8 +#define KEYCODE_LEFTARROW 0xf702 +#define KEYCODE_RIGHTARROW 0xf703 +#define KEYCODE_UPARROW 0xf700 +#define KEYCODE_DOWNARROW 0xf701 + + EventSender::EventSender(QWebPage* parent) : QObject(parent) { @@ -129,7 +138,7 @@ void EventSender::keyDown(const QString& string, const QStringList& modifiers) if (modifs == Qt::ShiftModifier) code = Qt::Key_Backtab; s = QString(); - } else if (code == 127) { + } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) { code = Qt::Key_Backspace; if (modifs == Qt::AltModifier) modifs = Qt::ControlModifier; @@ -148,28 +157,28 @@ void EventSender::keyDown(const QString& string, const QStringList& modifiers) s = QString(); code = Qt::Key_Home; modifs = 0; - } else if (code == 0xf702) { + } else if (code == KEYCODE_LEFTARROW) { s = QString(); code = Qt::Key_Left; if (modifs & Qt::MetaModifier) { code = Qt::Key_Home; modifs &= ~Qt::MetaModifier; } - } else if (code == 0xf703) { + } else if (code == KEYCODE_RIGHTARROW) { s = QString(); code = Qt::Key_Right; if (modifs & Qt::MetaModifier) { code = Qt::Key_End; modifs &= ~Qt::MetaModifier; } - } else if (code == 0xf700) { + } else if (code == KEYCODE_UPARROW) { s = QString(); code = Qt::Key_Up; if (modifs & Qt::MetaModifier) { code = Qt::Key_PageUp; modifs &= ~Qt::MetaModifier; } - } else if (code == 0xf701) { + } else if (code == KEYCODE_DOWNARROW) { s = QString(); code = Qt::Key_Down; if (modifs & Qt::MetaModifier) { diff --git a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro index 11ee1fa..636835a 100644 --- a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro +++ b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro @@ -13,6 +13,3 @@ unix:!mac { QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR } -lessThan(QT_MINOR_VERSION, 4) { - DEFINES += QT_BEGIN_NAMESPACE="" QT_END_NAMESPACE="" -} diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp index b8de6a5..0ea5632 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp @@ -32,13 +32,16 @@ #include "DumpRenderTree.h" #include "WorkQueue.h" #include "WorkQueueItem.h" +#include <QDir> extern void qt_dump_editing_callbacks(bool b); extern void qt_dump_resource_load_callbacks(bool b); extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled); extern bool qt_drt_pauseAnimation(QWebFrame*, const QString& name, double time, const QString& elementId); extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString& name, double time, const QString& elementId); +extern bool qt_drt_pauseSVGAnimation(QWebFrame*, const QString& animationId, double time, const QString& elementId); extern int qt_drt_numberOfActiveAnimations(QWebFrame*); + extern void qt_drt_whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); extern QString qt_drt_counterValueForElementById(QWebFrame* qFrame, const QString& id); @@ -63,6 +66,7 @@ void LayoutTestController::reset() m_timeoutTimer.stop(); m_topLoadingFrame = 0; m_waitForPolicy = false; + m_handleErrorPages = false; qt_dump_editing_callbacks(false); qt_dump_resource_load_callbacks(false); } @@ -72,7 +76,7 @@ void LayoutTestController::processWork() // qDebug() << ">>>processWork"; // if we didn't start a new load, then we finished all the commands, so we're ready to dump state - if (!WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) { + if (WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) { emit done(); m_isLoading = false; } @@ -81,7 +85,7 @@ void LayoutTestController::processWork() // Called on loadFinished on mainFrame. void LayoutTestController::maybeDump(bool success) { - Q_ASSERT(sender() == m_topLoadingFrame); + Q_ASSERT(sender() == m_topLoadingFrame->page()); // as the function is called on loadFinished, the test might // already have dumped and thus no longer be active, thus @@ -107,7 +111,7 @@ void LayoutTestController::waitUntilDone() { //qDebug() << ">>>>waitForDone"; m_waitForDone = true; - m_timeoutTimer.start(11000, this); + m_timeoutTimer.start(15000, this); } QString LayoutTestController::counterValueForElementById(const QString& id) @@ -123,10 +127,14 @@ void LayoutTestController::keepWebHistory() void LayoutTestController::notifyDone() { qDebug() << ">>>>notifyDone"; + if (!m_timeoutTimer.isActive()) return; + m_timeoutTimer.stop(); emit done(); + + // FIXME: investigate why always resetting these result in timeouts m_isLoading = false; m_waitForDone = false; m_waitForPolicy = false; @@ -142,6 +150,12 @@ void LayoutTestController::clearBackForwardList() m_drt->webPage()->history()->clear(); } +QString LayoutTestController::pathToLocalResource(const QString& url) +{ + // Function introduced in r28690. + return QLatin1String("file://") + QUrl(url).toLocalFile(); +} + void LayoutTestController::dumpEditingCallbacks() { qDebug() << ">>>dumpEditingCallbacks"; @@ -179,10 +193,16 @@ void LayoutTestController::queueReload() WorkQueue::shared()->queue(new ReloadItem(m_drt->webPage())); } -void LayoutTestController::queueScript(const QString &url) +void LayoutTestController::queueLoadingScript(const QString& script) +{ + //qDebug() << ">>>queueLoadingScript" << script; + WorkQueue::shared()->queue(new LoadingScriptItem(script, m_drt->webPage())); +} + +void LayoutTestController::queueNonLoadingScript(const QString& script) { - //qDebug() << ">>>queueScript" << url; - WorkQueue::shared()->queue(new ScriptItem(url, m_drt->webPage())); + //qDebug() << ">>>queueNonLoadingScript" << script; + WorkQueue::shared()->queue(new NonLoadingScriptItem(script, m_drt->webPage())); } void LayoutTestController::provisionalLoad() @@ -195,7 +215,9 @@ void LayoutTestController::provisionalLoad() void LayoutTestController::timerEvent(QTimerEvent *ev) { if (ev->timerId() == m_timeoutTimer.timerId()) { - //qDebug() << ">>>>>>>>>>>>> timeout"; + const char* message = "FAIL: Timed out waiting for notifyDone to be called\n"; + fprintf(stderr, "%s", message); + fprintf(stdout, "%s", message); notifyDone(); } else QObject::timerEvent(ev); @@ -215,6 +237,22 @@ QString LayoutTestController::decodeHostName(const QString& host) return decoded; } +void LayoutTestController::showWebInspector() +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + m_drt->webPage()->webInspector()->show(); +} + +void LayoutTestController::hideWebInspector() +{ + m_drt->webPage()->webInspector()->hide(); +} + +void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, enabled); +} + void LayoutTestController::setJavaScriptProfilingEnabled(bool enable) { m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); @@ -260,6 +298,15 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& p return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId); } +bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& animationId, + double time, + const QString& elementId) +{ + QWebFrame* frame = m_drt->webPage()->mainFrame(); + Q_ASSERT(frame); + return qt_drt_pauseSVGAnimation(frame, animationId, time, elementId); +} + unsigned LayoutTestController::numberOfActiveAnimations() const { QWebFrame* frame = m_drt->webPage()->mainFrame(); @@ -313,4 +360,6 @@ void LayoutTestController::overridePreference(const QString& name, const QVarian settings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, value.toBool()); else if (name == "WebKitDefaultFontSize") settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt()); + else if (name == "WebKitUsesPageCachePreferenceKey") + QWebSettings::setMaximumPagesInCache(value.toInt()); } diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h index cab014c..ac8681f 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h @@ -63,6 +63,7 @@ public: bool shouldDumpDatabaseCallbacks() const { return m_dumpDatabaseCallbacks; } bool shouldDumpStatusCallbacks() const { return m_dumpStatusCallbacks; } bool shouldWaitUntilDone() const { return m_waitForDone; } + bool shouldHandleErrorPages() const { return m_handleErrorPages; } bool canOpenWindows() const { return m_canOpenWindows; } bool shouldDumpTitleChanges() const { return m_dumpTitleChanges; } bool waitForPolicy() const { return m_waitForPolicy; } @@ -87,22 +88,28 @@ public slots: void keepWebHistory(); void notifyDone(); void dumpBackForwardList() { m_dumpBackForwardList = true; } + void handleErrorPages() { m_handleErrorPages = true; } void dumpEditingCallbacks(); void dumpResourceLoadCallbacks(); void queueBackNavigation(int howFarBackward); void queueForwardNavigation(int howFarForward); void queueLoad(const QString& url, const QString& target = QString()); void queueReload(); - void queueScript(const QString& url); + void queueLoadingScript(const QString& script); + void queueNonLoadingScript(const QString& script); void provisionalLoad(); void setCloseRemainingWindowsWhenComplete(bool = false) {} int windowCount(); void display() {} void clearBackForwardList(); + QString pathToLocalResource(const QString& url); void dumpTitleChanges() { m_dumpTitleChanges = true; } QString encodeHostName(const QString& host); QString decodeHostName(const QString& host); void dumpSelectionRect() const {} + void showWebInspector(); + void hideWebInspector(); + void setAllowUniversalAccessFromFileURLs(bool enable); void setJavaScriptProfilingEnabled(bool enable); void setFixedContentsSize(int width, int height); void setPrivateBrowsingEnabled(bool enable); @@ -111,6 +118,8 @@ public slots: bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId); bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId); + bool sampleSVGAnimationForElementAtTime(const QString& animationId, double time, const QString& elementId); + unsigned numberOfActiveAnimations() const; void whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); @@ -138,6 +147,7 @@ private: bool m_dumpDatabaseCallbacks; bool m_dumpStatusCallbacks; bool m_waitForPolicy; + bool m_handleErrorPages; QBasicTimer m_timeoutTimer; QWebFrame* m_topLoadingFrame; diff --git a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro index 7f502f8..7b8162b 100644 --- a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro +++ b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro @@ -1,7 +1,7 @@ TEMPLATE = lib TARGET = TestNetscapePlugIn -VPATH = ../../gtk/TestNetscapePlugin ../../TestNetscapePlugIn.subproj +VPATH = ../../unix/TestNetscapePlugin ../../TestNetscapePlugIn.subproj include(../../../../WebKit.pri) DESTDIR = $$OUTPUT_DIR/lib/plugins @@ -18,8 +18,8 @@ mac { } INCLUDEPATH += ../../../../JavaScriptCore \ - ../../gtk/TestNetscapePlugin/ForwardingHeaders \ - ../../gtk/TestNetscapePlugin/ForwardingHeaders/WebKit \ + ../../unix/TestNetscapePlugin/ForwardingHeaders \ + ../../unix/TestNetscapePlugin/ForwardingHeaders/WebKit \ ../../../../WebCore \ ../../../../WebCore/bridge \ ../../TestNetscapePlugIn.subproj @@ -31,5 +31,5 @@ mac { SOURCES += ../../TestNetscapePlugIn.subproj/main.cpp LIBS += -framework Carbon } else { - SOURCES += ../../gtk/TestNetscapePlugin/TestNetscapePlugin.cpp + SOURCES += ../../unix/TestNetscapePlugin/TestNetscapePlugin.cpp } diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp b/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp deleted file mode 100644 index f8448e4..0000000 --- a/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "WorkQueue.h" - -static const unsigned queueLength = 1024; - -static WorkQueueItem* theQueue[queueLength]; -static unsigned startOfQueue; -static unsigned endOfQueue; - -WorkQueue* WorkQueue::shared() -{ - static WorkQueue* sharedInstance = new WorkQueue; - return sharedInstance; -} - -WorkQueue::WorkQueue() - : m_frozen(false) -{ -} - -void WorkQueue::queue(WorkQueueItem* item) -{ - Q_ASSERT(endOfQueue < queueLength); - Q_ASSERT(endOfQueue >= startOfQueue); - - if (m_frozen) { - delete item; - return; - } - - theQueue[endOfQueue++] = item; -} - -WorkQueueItem* WorkQueue::dequeue() -{ - Q_ASSERT(endOfQueue >= startOfQueue); - - if (startOfQueue == endOfQueue) - return 0; - - return theQueue[startOfQueue++]; -} - -unsigned WorkQueue::count() -{ - return endOfQueue - startOfQueue; -} - -void WorkQueue::clear() -{ - for (unsigned i = startOfQueue; i < endOfQueue; ++i) { - delete theQueue[i]; - theQueue[i] = 0; - } - - startOfQueue = 0; - endOfQueue = 0; -} - -bool WorkQueue::processWork() -{ - bool startedLoad = false; - - while (!startedLoad && count()) { - WorkQueueItem* item = dequeue(); - Q_ASSERT(item); - startedLoad = item->invoke(); - delete item; - } - - // If we're done and we didn't start a load, then we're really done, so return true. - return !startedLoad; -} diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueue.h b/WebKitTools/DumpRenderTree/qt/WorkQueue.h deleted file mode 100644 index 902ba0d..0000000 --- a/WebKitTools/DumpRenderTree/qt/WorkQueue.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WorkQueue_h -#define WorkQueue_h - -#include "WorkQueueItem.h" - -class WorkQueue { -public: - static WorkQueue* shared(); - - void queue(WorkQueueItem*); - WorkQueueItem* dequeue(); - void clear(); - unsigned count(); - - void setFrozen(bool b) { m_frozen = b; } - - bool processWork(); // Returns true if all work is done, false if we started a load. - -private: - WorkQueue(); - - bool m_frozen; -}; - -#endif // !defined(WorkQueue_h) diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h b/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h index 9819ec0..d534493 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h +++ b/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h @@ -89,6 +89,27 @@ private: QString m_script; }; +class LoadingScriptItem : public ScriptItem { +public: + LoadingScriptItem(const QString& script, QWebPage* page) + : ScriptItem(script, page) + { + } + + virtual bool invoke() const { return ScriptItem::invoke(); } +}; + +class NonLoadingScriptItem : public ScriptItem { +public: + NonLoadingScriptItem(const QString& script, QWebPage* page) + : ScriptItem(script, page) + { + } + + virtual bool invoke() const { ScriptItem::invoke(); return false; } +}; + + class BackForwardItem : public WorkQueueItem { public: virtual bool invoke() const; diff --git a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp b/WebKitTools/DumpRenderTree/qt/jsobjects.cpp index e747aeb..af331f0 100644 --- a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp +++ b/WebKitTools/DumpRenderTree/qt/jsobjects.cpp @@ -26,7 +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. */ -#include <jsobjects.h> + +#include "config.h" +#include "jsobjects.h" + #include <qwebpage.h> GCController::GCController(QWebPage* parent) diff --git a/WebKitTools/DumpRenderTree/qt/main.cpp b/WebKitTools/DumpRenderTree/qt/main.cpp index ae67a59..719315f 100644 --- a/WebKitTools/DumpRenderTree/qt/main.cpp +++ b/WebKitTools/DumpRenderTree/qt/main.cpp @@ -29,6 +29,8 @@ #include "DumpRenderTree.h" +#include <wtf/AlwaysInline.h> + #include <qstringlist.h> #include <qapplication.h> #include <qurl.h> @@ -38,6 +40,7 @@ #include <qwebsettings.h> #include <qwebdatabase.h> #include <qdesktopservices.h> +#include <qwindowsstyle.h> #ifdef Q_WS_X11 #include <qx11info_x11.h> @@ -86,7 +89,7 @@ QString get_backtrace() { return s; } -static void crashHandler(int sig) +static NO_RETURN void crashHandler(int sig) { fprintf(stderr, "%s\n", strsignal(sig)); fprintf(stderr, "%s\n", get_backtrace().toLatin1().constData()); @@ -98,23 +101,25 @@ int main(int argc, char* argv[]) #ifdef Q_WS_X11 FcInit(); WebCore::DumpRenderTree::initializeFonts(); +#endif + #if QT_VERSION >= 0x040500 QApplication::setGraphicsSystem("raster"); #endif -#endif - QApplication app(argc, argv); -#ifdef Q_WS_X11 - QX11Info::setAppDpiY(0, 96); - QX11Info::setAppDpiX(0, 96); -#endif + + QApplication::setStyle(new QWindowsStyle); QFont f("Sans Serif"); f.setPointSize(9); f.setWeight(QFont::Normal); f.setStyle(QFont::StyleNormal); - app.setFont(f); - app.setStyle(QLatin1String("Plastique")); + QApplication::setFont(f); + QApplication app(argc, argv); +#ifdef Q_WS_X11 + QX11Info::setAppDpiY(0, 96); + QX11Info::setAppDpiX(0, 96); +#endif signal(SIGILL, crashHandler); /* 4: illegal instruction (not reset when caught) */ signal(SIGTRAP, crashHandler); /* 5: trace trap (not reset when caught) */ diff --git a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h index 08706f7..08706f7 100644 --- a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h +++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h diff --git a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h index 54a603d..54a603d 100644 --- a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h +++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h diff --git a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h index 00bbc18..00bbc18 100644 --- a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h +++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h diff --git a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/TestNetscapePlugin.cpp b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp index 6c62a7c..cb01267 100644 --- a/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/TestNetscapePlugin.cpp +++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp @@ -45,6 +45,8 @@ extern "C" { char* NP_GetMIMEDescription(void); } +static void executeScript(const PluginObject* obj, const char* script); + static NPError webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/, NPP instance, @@ -75,9 +77,15 @@ webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/, for (int i = 0; i < argc; i++) if (strcasecmp(argn[i], "src") == 0) pluginLog(instance, "src: %s", argv[i]); - } + } else if (strcasecmp(argn[i], "cleardocumentduringnew") == 0) + executeScript(obj, "document.body.innerHTML = ''"); + else if (!strcasecmp(argn[i], "ondestroy")) + obj->onDestroy = strdup(argv[i]); + else if (strcasecmp(argn[i], "testdocumentopenindestroystream") == 0) + obj->testDocumentOpenInDestroyStream = TRUE; + else if (strcasecmp(argn[i], "testwindowopen") == 0) + obj->testWindowOpen = TRUE; } - instance->pdata = obj; } @@ -89,6 +97,11 @@ webkit_test_plugin_destroy_instance(NPP instance, NPSavedData** /*save*/) { PluginObject* obj = static_cast<PluginObject*>(instance->pdata); if (obj) { + if (obj->onDestroy) { + executeScript(obj, obj->onDestroy); + free(obj->onDestroy); + } + if (obj->onStreamLoad) free(obj->onStreamLoad); @@ -117,6 +130,12 @@ webkit_test_plugin_set_window(NPP instance, NPWindow *window) pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height); obj->logSetWindow = false; } + + if (obj->testWindowOpen) { + testWindowOpen(instance); + obj->testWindowOpen = FALSE; + } + } return NPERR_NO_ERROR; @@ -167,6 +186,11 @@ webkit_test_plugin_destroy_stream(NPP instance, NPStream* /*stream*/, NPError /* if (obj->onStreamDestroy) executeScript(obj, obj->onStreamDestroy); + if (obj->testDocumentOpenInDestroyStream) { + testDocumentOpen(instance); + obj->testDocumentOpenInDestroyStream = FALSE; + } + return NPERR_NO_ERROR; } diff --git a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp index de3d9ff..163abb1 100644 --- a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp +++ b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp @@ -151,10 +151,24 @@ JSStringRef AccessibilityUIElement::role() VARIANT vRole; if (FAILED(m_element->get_accRole(self(), &vRole))) return JSStringCreateWithCharacters(0, 0); - ASSERT(V_VT(&vRole) == VT_I4); - TCHAR roleText[64] = {0}; - ::GetRoleText(V_I4(&vRole), roleText, ARRAYSIZE(roleText)); - return JSStringCreateWithCharacters(roleText, _tcslen(roleText)); + + ASSERT(V_VT(&vRole) == VT_I4 || V_VT(&vRole) == VT_BSTR); + + wstring result; + if (V_VT(&vRole) == VT_I4) { + unsigned roleTextLength = ::GetRoleText(V_I4(&vRole), 0, 0) + 1; + + Vector<TCHAR> roleText(roleTextLength); + + ::GetRoleText(V_I4(&vRole), roleText.data(), roleTextLength); + + result = roleText.data(); + } else if (V_VT(&vRole) == VT_BSTR) + result = wstring(V_BSTR(&vRole), ::SysStringLen(V_BSTR(&vRole))); + + ::VariantClear(&vRole); + + return JSStringCreateWithCharacters(result.data(), result.length()); } JSStringRef AccessibilityUIElement::subrole() @@ -162,6 +176,11 @@ JSStringRef AccessibilityUIElement::subrole() return 0; } +JSStringRef AccessibilityUIElement::roleDescription() +{ + return 0; +} + JSStringRef AccessibilityUIElement::title() { BSTR titleBSTR; @@ -175,13 +194,18 @@ JSStringRef AccessibilityUIElement::title() JSStringRef AccessibilityUIElement::description() { BSTR descriptionBSTR; - if (FAILED(m_element->get_accName(self(), &descriptionBSTR)) || !descriptionBSTR) + if (FAILED(m_element->get_accDescription(self(), &descriptionBSTR)) || !descriptionBSTR) return JSStringCreateWithCharacters(0, 0); wstring description(descriptionBSTR, SysStringLen(descriptionBSTR)); ::SysFreeString(descriptionBSTR); return JSStringCreateWithCharacters(description.data(), description.length()); } +JSStringRef AccessibilityUIElement::stringValue() +{ + return JSStringCreateWithCharacters(0, 0); +} + JSStringRef AccessibilityUIElement::language() { return JSStringCreateWithCharacters(0, 0); @@ -233,6 +257,35 @@ JSStringRef AccessibilityUIElement::valueDescription() { return 0; } +bool AccessibilityUIElement::isSelected() const +{ + return false; +} + +int AccessibilityUIElement::hierarchicalLevel() const +{ + return 0; +} + +bool AccessibilityUIElement::ariaIsGrabbed() const +{ + return false; +} + +JSStringRef AccessibilityUIElement::ariaDropEffects() const +{ + return 0; +} + +bool AccessibilityUIElement::isExpanded() const +{ + return false; +} + +JSStringRef AccessibilityUIElement::orientation() const +{ + return 0; +} double AccessibilityUIElement::intValue() { @@ -270,6 +323,7 @@ bool AccessibilityUIElement::isRequired() const return false; } + int AccessibilityUIElement::insertionPointLineNumber() { return 0; @@ -359,6 +413,11 @@ bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) return false; } +bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) +{ + return false; +} + void AccessibilityUIElement::increment() { } @@ -366,3 +425,55 @@ void AccessibilityUIElement::increment() void AccessibilityUIElement::decrement() { } + +void AccessibilityUIElement::showMenu() +{ +} + +AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index) +{ + return 0; +} + +AccessibilityUIElement AccessibilityUIElement::disclosedByRow() +{ + return 0; +} + +JSStringRef AccessibilityUIElement::accessibilityValue() const +{ + BSTR valueBSTR; + if (FAILED(m_element->get_accValue(self(), &valueBSTR)) || !valueBSTR) + return JSStringCreateWithCharacters(0, 0); + + wstring value(valueBSTR, SysStringLen(valueBSTR)); + ::SysFreeString(valueBSTR); + + return JSStringCreateWithCharacters(value.data(), value.length()); +} + + +JSStringRef AccessibilityUIElement::documentEncoding() +{ + return JSStringCreateWithCharacters(0, 0); +} + +JSStringRef AccessibilityUIElement::documentURI() +{ + return JSStringCreateWithCharacters(0, 0); +} diff --git a/WebKitTools/DumpRenderTree/win/DRTDesktopNotificationPresenter.cpp b/WebKitTools/DumpRenderTree/win/DRTDesktopNotificationPresenter.cpp index b18b724..1bc4678 100644 --- a/WebKitTools/DumpRenderTree/win/DRTDesktopNotificationPresenter.cpp +++ b/WebKitTools/DumpRenderTree/win/DRTDesktopNotificationPresenter.cpp @@ -28,6 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include "DRTDesktopNotificationPresenter.h" #include "DumpRenderTree.h" diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp index d486d06..1315f9e 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp @@ -66,7 +66,7 @@ using namespace std; -#ifndef NDEBUG +#if !defined(NDEBUG) && (!defined(DEBUG_INTERNAL) || defined(DEBUG_ALL)) const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug"; #else const LPWSTR TestPluginDir = L"TestNetscapePlugin"; @@ -201,6 +201,43 @@ static const wstring& fontsPath() return path; } +static void addQTDirToPATH() +{ + static LPCWSTR pathEnvironmentVariable = L"PATH"; + static LPCWSTR quickTimeKeyName = L"Software\\Apple Computer, Inc.\\QuickTime"; + static LPCWSTR quickTimeSysDir = L"QTSysDir"; + static bool initialized; + + if (initialized) + return; + initialized = true; + + // Get the QuickTime dll directory from the registry. The key can be in either HKLM or HKCU. + WCHAR qtPath[MAX_PATH]; + DWORD qtPathBufferLen = sizeof(qtPath); + DWORD keyType; + HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); + if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) { + qtPathBufferLen = sizeof(qtPath); + result = SHGetValue(HKEY_CURRENT_USER, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); + if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) + return; + } + + // Read the current PATH. + DWORD pathSize = GetEnvironmentVariableW(pathEnvironmentVariable, 0, 0); + Vector<WCHAR> oldPath(pathSize); + if (!GetEnvironmentVariableW(pathEnvironmentVariable, oldPath.data(), oldPath.size())) + return; + + // And add the QuickTime dll. + wstring newPath; + newPath.append(qtPath); + newPath.append(L";"); + newPath.append(oldPath.data(), oldPath.size()); + SetEnvironmentVariableW(pathEnvironmentVariable, newPath.data()); +} + #ifdef DEBUG_ALL #define WEBKITDLL TEXT("WebKit_debug.dll") #else @@ -263,6 +300,10 @@ static void initialize() for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i) textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str()); + // Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems + // linked with older versions of qtmlclientlib.dll. + addQTDirToPATH(); + // Register a host window WNDCLASSEX wcex; @@ -286,7 +327,7 @@ static void initialize() void displayWebView() { ::InvalidateRect(webViewWindow, 0, TRUE); - ::UpdateWindow(webViewWindow); + ::SendMessage(webViewWindow, WM_PAINT, 0, 0); } void dumpFrameScrollPosition(IWebFrame* frame) @@ -688,6 +729,11 @@ static bool shouldLogHistoryDelegates(const char* pathOrURL) return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\"); } +static bool shouldOpenWebInspector(const char* pathOrURL) +{ + return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\"); +} + static void resetDefaultsToConsistentValues(IWebPreferences* preferences) { #ifdef USE_MAC_FONTS @@ -793,6 +839,12 @@ static void resetWebViewToConsistentStateBeforeTesting() webViewPrivate->clearMainFrameName(); webViewPrivate->resetOriginAccessWhiteLists(); + BSTR groupName; + if (SUCCEEDED(webView->groupName(&groupName))) { + webViewPrivate->removeAllUserContentFromGroup(groupName); + SysFreeString(groupName); + } + sharedUIDelegate->resetUndoManager(); sharedFrameLoadDelegate->resetToConsistentState(); @@ -859,6 +911,9 @@ static void runTest(const string& testPathOrURL) resetWebViewToConsistentStateBeforeTesting(); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + prevTestBFItem = 0; if (webView) { COMPtr<IWebBackForwardList> bfList; @@ -893,6 +948,9 @@ static void runTest(const string& testPathOrURL) DispatchMessage(&msg); } + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->closeWebInspector(); + resetWebViewToConsistentStateBeforeTesting(); frame->stopLoading(); diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj b/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj index ba3640c..299c53c 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj @@ -84,7 +84,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
<Configuration
@@ -157,7 +157,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
<Configuration
@@ -228,7 +228,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
<Configuration
@@ -300,7 +300,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
<Configuration
@@ -373,7 +373,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
<Configuration
@@ -444,7 +444,7 @@ />
<Tool
Name="VCPostBuildEventTool"
- CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

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

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

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

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb"xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
</Configurations>
diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h b/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h index 6eb468d..54ec87b 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h @@ -30,6 +30,7 @@ #define DumpRenderTreeWin_h struct IWebFrame; +struct IWebScriptWorld; struct IWebView; struct PolicyDelegate; typedef const struct __CFString* CFStringRef; @@ -55,6 +56,8 @@ WindowToWebViewMap& windowToWebViewMap(); void setPersistentUserStyleSheetLocation(CFStringRef); bool setAlwaysAcceptCookies(bool alwaysAcceptCookies); +unsigned worldIDForWorld(IWebScriptWorld*); + extern UINT_PTR waitToDumpWatchdog; #endif // DumpRenderTreeWin_h diff --git a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp index 939090a..37d5e1c 100644 --- a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp @@ -289,12 +289,53 @@ HRESULT STDMETHODCALLTYPE FrameLoadDelegate::willCloseFrame( return E_NOTIMPL; } -HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didClearWindowObject( - /* [in] */ IWebView*webView, - /* [in] */ JSContextRef context, - /* [in] */ JSObjectRef windowObject, - /* [in] */ IWebFrame* frame) +HRESULT FrameLoadDelegate::didClearWindowObject(IWebView*, JSContextRef, JSObjectRef, IWebFrame*) +{ + return E_NOTIMPL; +} + +HRESULT FrameLoadDelegate::didClearWindowObjectForFrameInScriptWorld(IWebView* webView, IWebFrame* frame, IWebScriptWorld* world) +{ + ASSERT_ARG(webView, webView); + ASSERT_ARG(frame, frame); + ASSERT_ARG(world, world); + if (!webView || !frame || !world) + return E_POINTER; + + COMPtr<IWebScriptWorld> standardWorld; + if (FAILED(world->standardWorld(&standardWorld))) + return S_OK; + + if (world == standardWorld) + didClearWindowObjectForFrameInStandardWorld(frame); + else + didClearWindowObjectForFrameInIsolatedWorld(frame, world); + return S_OK; +} + +void FrameLoadDelegate::didClearWindowObjectForFrameInIsolatedWorld(IWebFrame* frame, IWebScriptWorld* world) +{ + COMPtr<IWebFramePrivate> framePrivate(Query, frame); + if (!framePrivate) + return; + + JSGlobalContextRef ctx = framePrivate->globalContextForScriptWorld(world); + if (!ctx) + return; + + JSObjectRef globalObject = JSContextGetGlobalObject(ctx); + if (!globalObject) + return; + + JSObjectSetProperty(ctx, globalObject, JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString("__worldID")).get(), JSValueMakeNumber(ctx, worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0); + return; +} + +void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* frame) { + JSGlobalContextRef context = frame->globalContext(); + JSObjectRef windowObject = JSContextGetGlobalObject(context); + JSValueRef exception = 0; ::gLayoutTestController->makeWindowObject(context, windowObject, &exception); @@ -310,8 +351,6 @@ HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didClearWindowObject( JSValueRef eventSender = makeEventSender(context); JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0); JSStringRelease(eventSenderStr); - - return S_OK; } HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFinishDocumentLoadForFrame( diff --git a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.h b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.h index 56325e2..cc6653b 100644 --- a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.h +++ b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.h @@ -141,7 +141,24 @@ public: /* [in] */ IWebView *sender, /* [in] */ IWebSecurityOrigin *origin); -protected: + virtual HRESULT STDMETHODCALLTYPE didClearWindowObjectForFrameInScriptWorld(IWebView*, IWebFrame*, IWebScriptWorld*); + + virtual HRESULT STDMETHODCALLTYPE didPushStateWithinPageForFrame( + /* [in] */ IWebView *sender, + /* [in] */ IWebFrame *frame) { return E_NOTIMPL; } + + virtual HRESULT STDMETHODCALLTYPE didReplaceStateWithinPageForFrame( + /* [in] */ IWebView *sender, + /* [in] */ IWebFrame *frame) { return E_NOTIMPL; } + + virtual HRESULT STDMETHODCALLTYPE didPopStateWithinPageForFrame( + /* [in] */ IWebView *sender, + /* [in] */ IWebFrame *frame) { return E_NOTIMPL; } + +private: + void didClearWindowObjectForFrameInIsolatedWorld(IWebFrame*, IWebScriptWorld*); + void didClearWindowObjectForFrameInStandardWorld(IWebFrame*); + void locationChangeDone(IWebError*, IWebFrame*); ULONG m_refCount; diff --git a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp index d3cac7a..5debf16 100644 --- a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp +++ b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp @@ -372,6 +372,23 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) prefsPrivate->setXSSAuditorEnabled(enabled); } +void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) +{ + COMPtr<IWebView> webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr<IWebPreferences> preferences; + if (FAILED(webView->preferences(&preferences))) + return; + + COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences); + if (!prefsPrivate) + return; + + prefsPrivate->setAllowUniversalAccessFromFileURLs(enabled); +} + void LayoutTestController::setPopupBlockingEnabled(bool enabled) { COMPtr<IWebView> webView; @@ -398,6 +415,23 @@ void LayoutTestController::setTabKeyCyclesThroughElements(bool shouldCycle) viewPrivate->setTabKeyCyclesThroughElements(shouldCycle ? TRUE : FALSE); } +void LayoutTestController::setTimelineProfilingEnabled(bool flag) +{ + COMPtr<IWebView> webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr<IWebViewPrivate> viewPrivate; + if (FAILED(webView->QueryInterface(&viewPrivate))) + return; + + COMPtr<IWebInspector> inspector; + if (FAILED(viewPrivate->inspector(&inspector))) + return; + + inspector->setTimelineProfilingEnabled(flag); +} + void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) { // FIXME: Implement! @@ -827,6 +861,31 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef prop return SUCCEEDED(hr) && wasRunning; } +bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId) +{ + COMPtr<IDOMDocument> document; + if (FAILED(frame->DOMDocument(&document))) + return false; + + BSTR idBSTR = JSStringCopyBSTR(animationId); + COMPtr<IDOMElement> element; + HRESULT hr = document->getElementById(idBSTR, &element); + SysFreeString(idBSTR); + if (FAILED(hr)) + return false; + + COMPtr<IWebFramePrivate> framePrivate(Query, frame); + if (!framePrivate) + return false; + + BSTR elementIdBSTR = JSStringCopyBSTR(elementId); + BOOL wasRunning = FALSE; + hr = framePrivate->pauseSVGAnimation(elementIdBSTR, element.get(), time, &wasRunning); + SysFreeString(elementIdBSTR); + + return SUCCEEDED(hr) && wasRunning; +} + unsigned LayoutTestController::numberOfActiveAnimations() const { COMPtr<IWebFramePrivate> framePrivate(Query, frame); @@ -861,7 +920,11 @@ void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView)))) return; - webView->addUserScriptToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), 1, bstrT(source).GetBSTR(), 0, 0, 0, 0, 0, runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd); + COMPtr<IWebScriptWorld> world; + if (FAILED(WebKitCreateInstance(__uuidof(WebScriptWorld), 0, __uuidof(world), reinterpret_cast<void**>(&world)))) + return; + + webView->addUserScriptToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), world.get(), bstrT(source).GetBSTR(), 0, 0, 0, 0, 0, runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd); } @@ -871,7 +934,11 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView)))) return; - webView->addUserStyleSheetToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), 1, bstrT(source).GetBSTR(), 0, 0, 0, 0, 0); + COMPtr<IWebScriptWorld> world; + if (FAILED(WebKitCreateInstance(__uuidof(WebScriptWorld), 0, __uuidof(world), reinterpret_cast<void**>(&world)))) + return; + + webView->addUserStyleSheetToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), world.get(), bstrT(source).GetBSTR(), 0, 0, 0, 0, 0); } void LayoutTestController::showWebInspector() @@ -947,14 +1014,45 @@ void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef scrip inspectorPrivate->evaluateInFrontend(callId, bstrT(script).GetBSTR()); } -void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script) +typedef HashMap<unsigned, COMPtr<IWebScriptWorld> > WorldMap; +static WorldMap& worldMap() +{ + static WorldMap& map = *new WorldMap; + return map; +} + +unsigned worldIDForWorld(IWebScriptWorld* world) +{ + WorldMap::const_iterator end = worldMap().end(); + for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) { + if (it->second == world) + return it->first; + } + + return 0; +} + +void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) { COMPtr<IWebFramePrivate> framePrivate(Query, frame); if (!framePrivate) return; + // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world + // that is created once and cached forever. + COMPtr<IWebScriptWorld> world; + if (!worldID) { + if (FAILED(WebKitCreateInstance(__uuidof(WebScriptWorld), 0, __uuidof(world), reinterpret_cast<void**>(&world)))) + return; + } else { + COMPtr<IWebScriptWorld>& worldSlot = worldMap().add(worldID, 0).first->second; + if (!worldSlot && FAILED(WebKitCreateInstance(__uuidof(WebScriptWorld), 0, __uuidof(worldSlot), reinterpret_cast<void**>(&worldSlot)))) + return; + world = worldSlot; + } + BSTR result; - if (FAILED(framePrivate->stringByEvaluatingJavaScriptInIsolatedWorld(worldId, reinterpret_cast<OLE_HANDLE>(globalObject), bstrT(script).GetBSTR(), &result))) + if (FAILED(framePrivate->stringByEvaluatingJavaScriptInScriptWorld(world.get(), globalObject, bstrT(script).GetBSTR(), &result))) return; SysFreeString(result); } diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp index 7175d33..82b1d4d 100644 --- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp +++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp @@ -88,12 +88,15 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch for (int i = 0; i < argc; i++) if (_stricmp(argn[i], "src") == 0) pluginLog(instance, "src: %s", argv[i]); - } + } else if (_stricmp(argn[i], "testdocumentopenindestroystream") == 0) + obj->testDocumentOpenInDestroyStream = TRUE; + else if (_stricmp(argn[i], "testwindowopen") == 0) + obj->testWindowOpen = TRUE; } instance->pdata = obj; } - + return NPERR_NO_ERROR; } @@ -120,6 +123,15 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) NPError NPP_SetWindow(NPP instance, NPWindow *window) { + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + + if (obj) { + if (obj->testWindowOpen) { + testWindowOpen(instance); + obj->testWindowOpen = FALSE; + } + } + return NPERR_NO_ERROR; } @@ -149,7 +161,7 @@ NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool se if (obj->onStreamLoad) executeScript(obj, obj->onStreamLoad); - + return NPERR_NO_ERROR; } @@ -160,6 +172,10 @@ NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) if (obj->onStreamDestroy) executeScript(obj, obj->onStreamDestroy); + if (obj->testDocumentOpenInDestroyStream) { + testDocumentOpen(instance); + } + return NPERR_NO_ERROR; } diff --git a/WebKitTools/DumpRenderTree/wscript b/WebKitTools/DumpRenderTree/wscript new file mode 100644 index 0000000..29eca9a --- /dev/null +++ b/WebKitTools/DumpRenderTree/wscript @@ -0,0 +1,64 @@ +#! /usr/bin/env python + +# Copyright (C) 2009 Kevin Ollivier All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# wxBrowser sample app build script for the waf build system + +import sys + +from settings import * + +include_paths = [ + os.path.join(output_dir), + os.path.join(wk_root, 'JavaScriptCore'), + os.path.join(wk_root, 'WebCore'), + os.path.join(wk_root, 'WebKit', 'wx'), + '.', + 'wx' + ] +sources = [ + 'LayoutTestController.cpp', + 'WorkQueue.cpp', + 'wx/DumpRenderTreeWx.cpp', + 'wx/LayoutTestControllerWx.cpp', + 'wx/WorkQueueItemWx.cpp' + ] + +def set_options(opt): + common_set_options(opt) + +def configure(conf): + common_configure(conf) + +def build(bld): + obj = bld.new_task_gen( + features = 'cxx cprogram', + includes = ' '.join(include_paths), + source = sources, + target = 'DumpRenderTree', + uselib = 'WXWEBKIT WX ' + get_config(), + libpath = [output_dir], + uselib_local = '', + install_path = output_dir) + diff --git a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp index 72f85ae..2c46950 100644 --- a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp +++ b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp @@ -170,6 +170,11 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) // FIXME: implement } +void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) +{ + // FIXME: implement +} + void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag) { // FIXME: implement @@ -262,6 +267,12 @@ bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef anima return false; } +bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId) +{ + // FIXME: implement + return false; +} + void LayoutTestController::setCacheModel(int) { // FIXME: implement @@ -318,3 +329,28 @@ void LayoutTestController::removeAllVisitedLinks() { // FIXME: Implement this. } + +void LayoutTestController::setTimelineProfilingEnabled(bool enabled) +{ + +} + +void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script) +{ + +} + +void LayoutTestController::disableImageLoading() +{ + +} + +void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +{ + +} + +JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id) +{ + +} diff --git a/WebKitTools/GNUmakefile.am b/WebKitTools/GNUmakefile.am index c2029f3..4425196 100644 --- a/WebKitTools/GNUmakefile.am +++ b/WebKitTools/GNUmakefile.am @@ -113,7 +113,7 @@ dumprendertree_cppflags += \ TestNetscapePlugin_libtestnetscapeplugin_la_CPPFLAGS = \ -I$(srcdir)/WebKitTools/DumpRenderTree \ - -I$(srcdir)/WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders \ + -I$(srcdir)/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders \ -I$(srcdir)/WebCore \ -I$(srcdir)/WebCore/bridge \ -I$(srcdir)/WebCore/plugins \ @@ -122,17 +122,17 @@ TestNetscapePlugin_libtestnetscapeplugin_la_CPPFLAGS = \ $(javascriptcore_cppflags) TestNetscapePlugin_libtestnetscapeplugin_la_SOURCES = \ - WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h \ - WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h \ - WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h \ - WebKitTools/DumpRenderTree/gtk/TestNetscapePlugin/TestNetscapePlugin.cpp \ + WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npapi.h \ + WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npfunctions.h \ + WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/ForwardingHeaders/WebKit/npruntime.h \ + WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp \ WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp \ WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h \ WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/TestObject.cpp \ WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/TestObject.h TestNetscapePlugin_libtestnetscapeplugin_la_LDFLAGS = \ - -rpath ${shell pwd}/$(top_builddir)/TestNetscapePlugin/.libs \ + -rpath ${shell pwd}/$(top_builddir)/../unix/TestNetscapePlugin/.libs \ $(no_undefined) \ -avoid-version \ -module diff --git a/WebKitTools/CommitQueueStatus/app.yaml b/WebKitTools/QueueStatusServer/app.yaml index 2756112..2756112 100644 --- a/WebKitTools/CommitQueueStatus/app.yaml +++ b/WebKitTools/QueueStatusServer/app.yaml diff --git a/WebKitTools/CommitQueueStatus/filters/__init__.py b/WebKitTools/QueueStatusServer/filters/__init__.py index ef65bee..ef65bee 100644 --- a/WebKitTools/CommitQueueStatus/filters/__init__.py +++ b/WebKitTools/QueueStatusServer/filters/__init__.py diff --git a/WebKitTools/CommitQueueStatus/filters/webkit_extras.py b/WebKitTools/QueueStatusServer/filters/webkit_extras.py index 6a08727..6a08727 100644 --- a/WebKitTools/CommitQueueStatus/filters/webkit_extras.py +++ b/WebKitTools/QueueStatusServer/filters/webkit_extras.py diff --git a/WebKitTools/CommitQueueStatus/index.html b/WebKitTools/QueueStatusServer/index.html index 2b5aced..fbd51fb 100644 --- a/WebKitTools/CommitQueueStatus/index.html +++ b/WebKitTools/QueueStatusServer/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>WebKit Commit Queue Status</title> diff --git a/WebKitTools/CommitQueueStatus/index.yaml b/WebKitTools/QueueStatusServer/index.yaml index a3b9e05..bf11262 100644 --- a/WebKitTools/CommitQueueStatus/index.yaml +++ b/WebKitTools/QueueStatusServer/index.yaml @@ -9,3 +9,16 @@ 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: QueueStatus + properties: + - name: active_patch_id + - name: queue_name + - name: date + direction: desc + +- kind: QueueStatus + properties: + - name: queue_name + - name: date + direction: desc diff --git a/WebKitTools/CommitQueueStatus/queue_status.py b/WebKitTools/QueueStatusServer/queue_status.py index 30d2494..65197bb 100644 --- a/WebKitTools/CommitQueueStatus/queue_status.py +++ b/WebKitTools/QueueStatusServer/queue_status.py @@ -35,29 +35,91 @@ use_library('django', '1.1') from google.appengine.ext.webapp import template from google.appengine.api import users -from google.appengine.ext import webapp +from google.appengine.ext import webapp, db from google.appengine.ext.webapp.util import run_wsgi_app -from google.appengine.ext import db webapp.template.register_template_library('filters.webkit_extras') + class QueueStatus(db.Model): author = db.UserProperty() + queue_name = db.StringProperty() active_bug_id = db.IntegerProperty() active_patch_id = db.IntegerProperty() message = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True) + results_file = db.BlobProperty() + class MainPage(webapp.RequestHandler): def get(self): - statuses_query = QueueStatus.all().order('-date') + statuses_query = QueueStatus.all().filter('queue_name =', 'commit-queue').order('-date') statuses = statuses_query.fetch(6) + if not statuses: + return self.response.out.write("No status to report.") template_values = { 'last_status' : statuses[0], 'recent_statuses' : statuses[1:], } self.response.out.write(template.render('index.html', template_values)) + +class PatchStatus(webapp.RequestHandler): + def get(self, queue_name, attachment_id): + statuses = QueueStatus.all().filter('queue_name =', queue_name).filter('active_patch_id =', int(attachment_id)).order('-date').fetch(1) + if not statuses: + self.error(404) + return + self.response.out.write(statuses[0].message) + + +class StatusSummary(object): + def _status_to_code(self, status): + code_names = { + "Pass": "pass", + "Pending": "pending", + "Fail": "fail", + "Error": "error", + } + return code_names.get(status, "none") + + def _queue_name_to_code(self, queue_name): + code_names = { + "style-queue": "style", + } + return code_names[queue_name] + + queues = [ + "style-queue", + ] + + def __init__(self): + self._summary = {} + + def summarize(self, attachment_id): + if self._summary.get(attachment_id): + return self._summary.get(attachment_id) + + attachment_summary = {} + for queue in self.queues: + statuses = QueueStatus.all().filter('queue_name =', queue).filter('active_patch_id =', attachment_id).order('-date').fetch(1) + status_code = self._status_to_code(statuses[0].message if statuses else None) + queue_code = self._queue_name_to_code(queue) + attachment_summary[queue_code] = status_code + + self._summary[attachment_id] = attachment_summary + return attachment_summary + + +class StatusBubble(webapp.RequestHandler): + def get(self, attachment_id): + status_summary = StatusSummary() + template_values = { + "queue_status" : status_summary.summarize(int(attachment_id)), + } + self.response.out.write(template.render('status_bubble.html', template_values)) + + class UpdateStatus(webapp.RequestHandler): def get(self): self.response.out.write(template.render('update_status.html', None)) @@ -66,6 +128,7 @@ class UpdateStatus(webapp.RequestHandler): string_value = self.request.get(name) try: int_value = int(string_value) + return int_value except ValueError, TypeError: pass return None @@ -76,15 +139,33 @@ class UpdateStatus(webapp.RequestHandler): if users.get_current_user(): queue_status.author = users.get_current_user() + queue_name = self.request.get('queue_name') + queue_status.queue_name = queue_name queue_status.active_bug_id = self._int_from_request('bug_id') queue_status.active_patch_id = self._int_from_request('patch_id') queue_status.message = self.request.get('status') + results_file = self.request.get("results_file") + queue_status.results_file = db.Blob(results_file) queue_status.put() self.redirect('/') + +class ShowResults(webapp.RequestHandler): + def get(self, status_id): + status = QueueStatus.get_by_id(int(status_id)) + if not status: + self.error(404) + return + self.response.headers["Content-Type"] = "text/plain" + self.response.out.write(status.results_file) + + routes = [ ('/', MainPage), - ('/update_status', UpdateStatus) + ('/update-status', UpdateStatus), + (r'/patch-status/(.*)/(.*)', PatchStatus), + (r'/status-bubble/(.*)', StatusBubble), + (r'/results/(.*)', ShowResults) ] application = webapp.WSGIApplication(routes, debug=True) diff --git a/WebKitTools/QueueStatusServer/status_bubble.html b/WebKitTools/QueueStatusServer/status_bubble.html new file mode 100644 index 0000000..f8959d9 --- /dev/null +++ b/WebKitTools/QueueStatusServer/status_bubble.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> +<style> +body { + font-family: Verdana, sans-serif; + margin: 0px; + padding: 0px; +} +.status { + display: block; + float: left; + margin: 1px; + padding: 1px 2px; + border-radius: 5px; + border: 1px solid #AAA; + font-size: 11px; +} +.pass { + background-color: #8FDF5F; + border: 1px solid #4F8530; +} +.fail { + background-color: #E98080; + border: 1px solid #A77272; +} +.pending { + background-color: #FFFC6C; + border: 1px solid #C5C56D; +} +</style> +</head> +<body>{% for key, value in queue_status.items %} +<div class="status {{value}}" title="{{key}}: {{value}}">{{key}}</div>{% endfor %} +</body> +</html> diff --git a/WebKitTools/CommitQueueStatus/stylesheets/main.css b/WebKitTools/QueueStatusServer/stylesheets/main.css index 55d3694..55d3694 100644 --- a/WebKitTools/CommitQueueStatus/stylesheets/main.css +++ b/WebKitTools/QueueStatusServer/stylesheets/main.css diff --git a/WebKitTools/CommitQueueStatus/update_status.html b/WebKitTools/QueueStatusServer/update_status.html index edafba4..9343c60 100644 --- a/WebKitTools/CommitQueueStatus/update_status.html +++ b/WebKitTools/QueueStatusServer/update_status.html @@ -1,5 +1,5 @@ -Update the current status of the commit-queue: -<form name="update_status" method="post"> +<form name="update_status" enctype="multipart/form-data" method="post"> +Update status for a queue: <input name="queue_name"> <div> Active Bug Id: <input name="bug_id"> @@ -8,12 +8,10 @@ Update the current status of the commit-queue: Active Patch Id: <input name="patch_id"> </div> - <div> - Space separated list of other bugs in queue: - <input name="bugs_in_queue"> - </div> <div> + Status Text:<br> <textarea name="status" rows="3" cols="60"></textarea> </div> + <div>Results file: <input type="file" name="results_file"></div> <div><input type="submit" value="Add Status"></div> </form> diff --git a/WebKitTools/Scripts/VCSUtils.pm b/WebKitTools/Scripts/VCSUtils.pm index e1e0bc2..7638102 100644 --- a/WebKitTools/Scripts/VCSUtils.pm +++ b/WebKitTools/Scripts/VCSUtils.pm @@ -41,7 +41,10 @@ BEGIN { @ISA = qw(Exporter); @EXPORT = qw( &canonicalizePath + &changeLogEmailAddress + &changeLogName &chdirReturningRelativePath + &decodeGitBinaryPatch &determineSVNRoot &determineVCSRoot &fixChangeLogPatch @@ -298,6 +301,14 @@ sub canonicalizePath($) return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; } +sub removeEOL($) +{ + my ($line) = @_; + + $line =~ s/[\r\n]+$//g; + return $line; +} + sub svnStatus($) { my ($fullPath) = @_; @@ -335,8 +346,6 @@ sub gitdiff2svndiff($) $_ = shift @_; if (m#^diff --git a/(.+) b/(.+)#) { return "Index: $1"; - } elsif (m/^new file.*/) { - return ""; } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { return "==================================================================="; } elsif (m#^--- a/(.+)#) { @@ -347,56 +356,215 @@ sub gitdiff2svndiff($) return $_; } +# The diff(1) command is greedy when matching lines, so a new ChangeLog entry will +# have lines of context at the top of a patch when the existing entry has the same +# date and author as the new entry. Alter the ChangeLog patch so +# that the added lines ("+") in the patch always start at the beginning of the +# patch and there are no initial lines of context. sub fixChangeLogPatch($) { - my $patch = shift; - my $contextLineCount = 3; - - return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; - my ($oldLineCount, $newLineCount) = ($1, $2); - return $patch if $oldLineCount <= $contextLineCount; - - # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will - # have lines of context at the top of a patch when the existing entry has the same - # date and author as the new entry. This nifty loop alters a ChangeLog patch so - # that the added lines ("+") in the patch always start at the beginning of the - # patch and there are no initial lines of context. - my $newPatch; - my $lineCountInState = 0; - my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; - my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; - my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); - my $state = $stateHeader; - foreach my $line (split(/\n/, $patch)) { - $lineCountInState++; - if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { - $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; - $lineCountInState = 0; - $state = $statePreContext; - } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { - $line = "+" . substr($line, 1); - if ($lineCountInState == $oldContentLineCountReduction) { - $lineCountInState = 0; - $state = $stateNewChanges; - } - } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { - # No changes to these lines - if ($lineCountInState == $newContentLineCountWithoutContext) { - $lineCountInState = 0; - $state = $statePostContext; - } - } elsif ($state == $statePostContext) { - if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { - $line = " " . substr($line, 1); - } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { - next; # Discard + my $patch = shift; # $patch will only contain patch fragments for ChangeLog. + + $patch =~ /(\r?\n)/; + my $lineEnding = $1; + my @patchLines = split(/$lineEnding/, $patch); + + # e.g. 2009-06-03 Eric Seidel <eric@webkit.org> + my $dateLineRegexpString = '^\+(\d{4}-\d{2}-\d{2})' # Consume the leading '+' and the date. + . '\s+(.+)\s+' # Consume the name. + . '<([^<>]+)>$'; # And finally the email address. + + # Figure out where the patch contents start and stop. + my $patchHeaderIndex; + my $firstContentIndex; + my $trailingContextIndex; + my $dateIndex; + my $patchEndIndex = scalar(@patchLines); + for (my $index = 0; $index < @patchLines; ++$index) { + my $line = $patchLines[$index]; + if ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@$/) { # e.g. @@ -1,5 +1,18 @@ + if ($patchHeaderIndex) { + $patchEndIndex = $index; # We only bother to fix up the first patch fragment. + last; } + $patchHeaderIndex = $index; } - $newPatch .= $line . "\n"; + $firstContentIndex = $index if ($patchHeaderIndex && !$firstContentIndex && $line =~ /^\+[^+]/); # Only match after finding patchHeaderIndex, otherwise we'd match "+++". + $dateIndex = $index if ($line =~ /$dateLineRegexpString/); + $trailingContextIndex = $index if ($firstContentIndex && !$trailingContextIndex && $line =~ /^ /); } + my $contentLineCount = $trailingContextIndex - $firstContentIndex; + my $trailingContextLineCount = $patchEndIndex - $trailingContextIndex; + + # If we didn't find a date line in the content then this is not a patch we should try and fix. + return $patch if (!$dateIndex); + + # We only need to do anything if the date line is not the first content line. + return $patch if ($dateIndex == $firstContentIndex); + + # Write the new patch. + my $totalNewContentLines = $contentLineCount + $trailingContextLineCount; + $patchLines[$patchHeaderIndex] = "@@ -1,$trailingContextLineCount +1,$totalNewContentLines @@"; # Write a new header. + my @repeatedLines = splice(@patchLines, $dateIndex, $trailingContextIndex - $dateIndex); # The date line and all the content after it that diff saw as repeated. + splice(@patchLines, $firstContentIndex, 0, @repeatedLines); # Move the repeated content to the top. + foreach my $line (@repeatedLines) { + $line =~ s/^\+/ /; + } + splice(@patchLines, $trailingContextIndex, $patchEndIndex, @repeatedLines); # Replace trailing context with the repeated content. + splice(@patchLines, $patchHeaderIndex + 1, $firstContentIndex - $patchHeaderIndex - 1); # Remove any leading context. + + return join($lineEnding, @patchLines) . "\n"; # patch(1) expects an extra trailing newline. +} - return $newPatch; +sub gitConfig($) +{ + return unless $isGit; + + my ($config) = @_; + + my $result = `git config $config`; + if (($? >> 8)) { + $result = `git repo-config $config`; + } + chomp $result; + return $result; } +sub changeLogNameError($) +{ + my ($message) = @_; + print STDERR "$message\nEither:\n"; + print STDERR " set CHANGE_LOG_NAME in your environment\n"; + print STDERR " OR pass --name= on the command line\n"; + print STDERR " OR set REAL_NAME in your environment"; + print STDERR " OR git users can set 'git config user.name'\n"; + exit(1); +} + +sub changeLogName() +{ + my $name = $ENV{CHANGE_LOG_NAME} || $ENV{REAL_NAME} || gitConfig("user.name") || (split /\s*,\s*/, (getpwuid $<)[6])[0]; + + changeLogNameError("Failed to determine ChangeLog name.") unless $name; + # getpwuid seems to always succeed on windows, returning the username instead of the full name. This check will catch that case. + changeLogNameError("'$name' does not contain a space! ChangeLogs should contain your full name.") unless ($name =~ /\w \w/); + + return $name; +} + +sub changeLogEmailAddressError($) +{ + my ($message) = @_; + print STDERR "$message\nEither:\n"; + print STDERR " set CHANGE_LOG_EMAIL_ADDRESS in your environment\n"; + print STDERR " OR pass --email= on the command line\n"; + print STDERR " OR set EMAIL_ADDRESS in your environment\n"; + print STDERR " OR git users can set 'git config user.email'\n"; + exit(1); +} + +sub changeLogEmailAddress() +{ + my $emailAddress = $ENV{CHANGE_LOG_EMAIL_ADDRESS} || $ENV{EMAIL_ADDRESS} || gitConfig("user.email"); + + changeLogEmailAddressError("Failed to determine email address for ChangeLog.") unless $emailAddress; + changeLogEmailAddressError("Email address '$emailAddress' does not contain '\@' and is likely invalid.") unless ($emailAddress =~ /\@/); + + return $emailAddress; +} + +# http://tools.ietf.org/html/rfc1924 +sub decodeBase85($) +{ + my ($encoded) = @_; + my %table; + my @characters = ('0'..'9', 'A'..'Z', 'a'..'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~'); + for (my $i = 0; $i < 85; $i++) { + $table{$characters[$i]} = $i; + } + + my $decoded = ''; + my @encodedChars = $encoded =~ /./g; + + for (my $encodedIter = 0; defined($encodedChars[$encodedIter]);) { + my $digit = 0; + for (my $i = 0; $i < 5; $i++) { + $digit *= 85; + my $char = $encodedChars[$encodedIter]; + $digit += $table{$char}; + $encodedIter++; + } + + for (my $i = 0; $i < 4; $i++) { + $decoded .= chr(($digit >> (3 - $i) * 8) & 255); + } + } + + return $decoded; +} + +sub decodeGitBinaryChunk($$) +{ + my ($contents, $fullPath) = @_; + + # Load this module lazily in case the user don't have this module + # and won't handle git binary patches. + require Compress::Zlib; + + my $encoded = ""; + my $compressedSize = 0; + while ($contents =~ /^([A-Za-z])(.*)$/gm) { + my $line = $2; + next if $line eq ""; + die "$fullPath: unexpected size of a line: $&" if length($2) % 5 != 0; + my $actualSize = length($2) / 5 * 4; + my $encodedExpectedSize = ord($1); + my $expectedSize = $encodedExpectedSize <= ord("Z") ? $encodedExpectedSize - ord("A") + 1 : $encodedExpectedSize - ord("a") + 27; + + die "$fullPath: unexpected size of a line: $&" if int(($expectedSize + 3) / 4) * 4 != $actualSize; + $compressedSize += $expectedSize; + $encoded .= $line; + } + + my $compressed = decodeBase85($encoded); + $compressed = substr($compressed, 0, $compressedSize); + return Compress::Zlib::uncompress($compressed); +} + +sub decodeGitBinaryPatch($$) +{ + my ($contents, $fullPath) = @_; + + # Git binary patch has two chunks. One is for the normal patching + # and another is for the reverse patching. + # + # Each chunk a line which starts from either "literal" or "delta", + # followed by a number which specifies decoded size of the chunk. + # The "delta" type chunks aren't supported by this function yet. + # + # Then, content of the chunk comes. To decode the content, we + # need decode it with base85 first, and then zlib. + my $gitPatchRegExp = '(literal|delta) ([0-9]+)\n([A-Za-z0-9!#$%&()*+-;<=>?@^_`{|}~\\n]*?)\n\n'; + if ($contents !~ m"\nGIT binary patch\n$gitPatchRegExp$gitPatchRegExp\Z") { + die "$fullPath: unknown git binary patch format" + } + + my $binaryChunkType = $1; + my $binaryChunkExpectedSize = $2; + my $encodedChunk = $3; + my $reverseBinaryChunkType = $4; + my $reverseBinaryChunkExpectedSize = $5; + my $encodedReverseChunk = $6; + + my $binaryChunk = decodeGitBinaryChunk($encodedChunk, $fullPath); + my $binaryChunkActualSize = length($binaryChunk); + my $reverseBinaryChunk = decodeGitBinaryChunk($encodedReverseChunk, $fullPath); + my $reverseBinaryChunkActualSize = length($reverseBinaryChunk); + + die "$fullPath: unexpected size of the first chunk (expected $binaryChunkExpectedSize but was $binaryChunkActualSize" if ($binaryChunkExpectedSize != $binaryChunkActualSize); + die "$fullPath: unexpected size of the second chunk (expected $reverseBinaryChunkExpectedSize but was $reverseBinaryChunkActualSize" if ($reverseBinaryChunkExpectedSize != $reverseBinaryChunkActualSize); + + return ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk); +} 1; diff --git a/WebKitTools/Scripts/bisect-builds b/WebKitTools/Scripts/bisect-builds index 55bf238..063b61e 100755 --- a/WebKitTools/Scripts/bisect-builds +++ b/WebKitTools/Scripts/bisect-builds @@ -363,12 +363,13 @@ sub mountAndRunNightly($$$$) my $mountPath = "/Volumes/WebKit"; my $webkitApp = File::Spec->catfile($mountPath, "WebKit.app"); my $diskImage = File::Spec->catfile($directory, $filename); + my $devNull = File::Spec->devnull(); my $i = 0; while (-e $mountPath) { $i++; usleep 100 if $i > 1; - exec "hdiutil", "detach '$mountPath' 2> " . File::Spec->devnull(); + `hdiutil detach '$mountPath' 2> $devNull`; die "Could not unmount $diskImage at $mountPath" if $i > 100; } die "Can't mount $diskImage: $mountPath already exists!" if -e $mountPath; @@ -393,7 +394,7 @@ sub mountAndRunNightly($$$$) $tempFile ||= ""; `DYLD_FRAMEWORK_PATH=$frameworkPath WEBKIT_UNSET_DYLD_FRAMEWORK_PATH=YES $safari $tempFile`; - exec "hdiutil", "detach '$mountPath' 2> " . File::Spec->devnull(); + `hdiutil detach '$mountPath' 2> $devNull`; } sub parseRevisions($$;$) diff --git a/WebKitTools/Scripts/bugzilla-tool b/WebKitTools/Scripts/bugzilla-tool index 8e899b5..fdbb740 100755 --- a/WebKitTools/Scripts/bugzilla-tool +++ b/WebKitTools/Scripts/bugzilla-tool @@ -31,894 +31,74 @@ # A tool for automating dealing with bugzilla, posting patches, committing patches, etc. import os -import re -import StringIO # for add_patch_to_bug file wrappers -import subprocess -import sys -import time -from datetime import datetime, timedelta -from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option - -# Import WebKit-specific modules. -from modules.bugzilla import Bugzilla, parse_bug_id -from modules.changelogs import ChangeLog -from modules.comments import bug_comment_from_commit_text -from modules.logging import error, log, tee -from modules.scm import CommitMessage, detect_scm_system, ScriptError, CheckoutNeedsUpdate +from modules.bugzilla import Bugzilla from modules.buildbot import BuildBot -from modules.statusbot import StatusBot - -def plural(noun): - # This is a dumb plural() implementation which was just enough for our uses. - if re.search('h$', noun): - return noun + 'es' - else: - return noun + 's' - -def pluralize(noun, count): - if count != 1: - noun = plural(noun) - return "%d %s" % (count, noun) - -def commit_message_for_this_commit(scm): - changelog_paths = scm.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()) - - -class Command: - def __init__(self, help_text, argument_names="", options=[], requires_local_commits=False): - self.help_text = help_text - self.argument_names = argument_names - self.options = options - self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options) - self.requires_local_commits = requires_local_commits - - def name_with_arguments(self, command_name): - usage_string = command_name - if len(self.options) > 0: - usage_string += " [options]" - if self.argument_names: - usage_string += " " + self.argument_names - return usage_string - - def parse_args(self, args): - return self.option_parser.parse_args(args) - - def execute(self, options, args, tool): - raise NotImplementedError, "subclasses must implement" - - -class BugsInCommitQueue(Command): - def __init__(self): - Command.__init__(self, 'Bugs in the commit queue') - - def execute(self, options, args, tool): - bug_ids = tool.bugs.fetch_bug_ids_from_commit_queue() - for bug_id in bug_ids: - print "%s" % bug_id - - -class PatchesInCommitQueue(Command): - def __init__(self): - Command.__init__(self, 'Patches in the commit queue') - - def execute(self, options, args, tool): - patches = tool.bugs.fetch_patches_from_commit_queue() - log("Patches in commit queue:") - for patch in patches: - print "%s" % patch['url'] - - -class ReviewedPatchesOnBug(Command): - def __init__(self): - Command.__init__(self, 'r+\'d patches on a bug', 'BUGID') - - def execute(self, options, args, tool): - bug_id = args[0] - patches_to_land = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) - for patch in patches_to_land: - print "%s" % patch['url'] - - -class ApplyPatchesFromBug(Command): - def __init__(self): - options = [ - make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory before applying patches"), - make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch"), - ] - options += WebKitLandingScripts.cleaning_options() - Command.__init__(self, 'Applies all patches on a bug to the local working directory without committing.', 'BUGID', options=options) - - @staticmethod - def apply_patches(patches, scm, commit_each): - for patch in patches: - scm.apply_patch(patch) - if commit_each: - commit_message = commit_message_for_this_commit(scm) - scm.commit_locally_with_message(commit_message.message() or patch['name']) - - def execute(self, options, args, tool): - bug_id = args[0] - patches = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) - os.chdir(tool.scm().checkout_root) - if options.clean: - tool.scm().ensure_clean_working_directory(options.force_clean) - if options.update: - tool.scm().update_webkit() - - if options.local_commit and not tool.scm().supports_local_commits(): - error("--local-commit passed, but %s does not support local commits" % tool.scm().display_name()) - - self.apply_patches(patches, tool.scm(), options.local_commit) - - -class WebKitLandingScripts: - @staticmethod - def cleaning_options(): - return [ - make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)"), - make_option("--no-clean", action="store_false", dest="clean", default=True, help="Don't check if the working directory is clean before applying patches"), - ] - - @staticmethod - def land_options(): - return [ - 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."), - make_option("--no-close", action="store_false", dest="close_bug", default=True, help="Leave bug open after landing."), - make_option("--no-build", action="store_false", dest="build", default=True, help="Commit without building first, implies --no-test."), - make_option("--no-test", action="store_false", dest="test", default=True, help="Commit without running run-webkit-tests."), - make_option("--quiet", action="store_true", dest="quiet", default=False, help="Produce less console output."), - make_option("--commit-queue", action="store_true", dest="commit_queue", default=False, help="Run in commit queue mode (no user interaction)."), - ] - - @staticmethod - def run_command_with_teed_output(args, teed_output): - child_process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - # Use our own custom wait loop because Popen ignores a tee'd stderr/stdout. - # FIXME: This could be improved not to flatten output to stdout. - while True: - output_line = child_process.stdout.readline() - if output_line == '' and child_process.poll() != None: - return child_process.poll() - teed_output.write(output_line) - - @staticmethod - def run_and_throw_if_fail(args, quiet=False): - # Cache the child's output locally so it can be used for error reports. - child_out_file = StringIO.StringIO() - if quiet: - dev_null = open(os.devnull, "w") - child_stdout = tee(child_out_file, dev_null if quiet else sys.stdout) - exit_code = WebKitLandingScripts.run_command_with_teed_output(args, child_stdout) - if quiet: - dev_null.close() - - child_output = child_out_file.getvalue() - child_out_file.close() - - if exit_code: - raise ScriptError(script_args=args, exit_code=exit_code, output=child_output) - - # We might need to pass scm into this function for scm.checkout_root - @staticmethod - def webkit_script_path(script_name): - return os.path.join("WebKitTools", "Scripts", script_name) - - @classmethod - def run_webkit_script(cls, script_name, quiet=False): - log("Running %s" % script_name) - cls.run_and_throw_if_fail(cls.webkit_script_path(script_name), quiet) - - @classmethod - def build_webkit(cls, quiet=False): - cls.run_webkit_script("build-webkit", quiet) - - @staticmethod - def ensure_builders_are_green(buildbot, options): - if not options.check_builders or buildbot.core_builders_are_green(): - return - error("Builders at %s are red, please do not commit. Pass --ignore-builders to bypass this check." % (buildbot.buildbot_host)) - - @classmethod - def run_webkit_tests(cls, launch_safari, fail_fast=False, quiet=False): - args = [cls.webkit_script_path("run-webkit-tests")] - if not launch_safari: - args.append("--no-launch-safari") - if quiet: - args.append("--quiet") - if fail_fast: - args.append("--exit-after-n-failures=1") - cls.run_and_throw_if_fail(args) - - @staticmethod - def setup_for_landing(scm, options): - os.chdir(scm.checkout_root) - scm.ensure_no_local_commits(options.force_clean) - if options.clean: - scm.ensure_clean_working_directory(options.force_clean) - - @classmethod - def build_and_commit(cls, scm, options): - if options.build: - cls.build_webkit(quiet=options.quiet) - if options.test: - # When running the commit-queue we don't want to launch Safari and we want to exit after the first failure. - cls.run_webkit_tests(launch_safari=not options.commit_queue, fail_fast=options.commit_queue, quiet=options.quiet) - commit_message = commit_message_for_this_commit(scm) - commit_log = scm.commit_with_message(commit_message.message()) - return bug_comment_from_commit_text(scm, commit_log) - - -class LandAndUpdateBug(Command): - def __init__(self): - options = [ - make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER."), - ] - options += WebKitLandingScripts.land_options() - Command.__init__(self, 'Lands the current working directory diff and updates the bug if provided.', '[BUGID]', options=options) - - def guess_reviewer_from_bug(self, bugs, bug_id): - patches = bugs.fetch_reviewed_patches_from_bug(bug_id) - if len(patches) != 1: - log("%s on bug %s, cannot infer reviewer." % (pluralize("reviewed patch", len(patches)), bug_id)) - return None - patch = patches[0] - reviewer = patch['reviewer'] - log('Guessing "%s" as reviewer from attachment %s on bug %s.' % (reviewer, patch['id'], bug_id)) - return reviewer - - def update_changelogs_with_reviewer(self, reviewer, bug_id, tool): - if not reviewer: - if not bug_id: - log("No bug id provided and --reviewer= not provided. Not updating ChangeLogs with reviewer.") - return - reviewer = self.guess_reviewer_from_bug(tool.bugs, bug_id) - - if not reviewer: - log("Failed to guess reviewer from bug %s and --reviewer= not provided. Not updating ChangeLogs with reviewer." % bug_id) - return - - for changelog_path in tool.scm().modified_changelogs(): - ChangeLog(changelog_path).set_reviewer(reviewer) - - def execute(self, options, args, tool): - bug_id = args[0] if len(args) else None - os.chdir(tool.scm().checkout_root) - - WebKitLandingScripts.ensure_builders_are_green(tool.buildbot, options) - - self.update_changelogs_with_reviewer(options.reviewer, bug_id, tool) - - comment_text = WebKitLandingScripts.build_and_commit(tool.scm(), options) - if bug_id: - log("Updating bug %s" % bug_id) - if options.close_bug: - tool.bugs.close_bug_as_fixed(bug_id, comment_text) - else: - # FIXME: We should a smart way to figure out if the patch is attached - # to the bug, and if so obsolete it. - tool.bugs.post_comment_to_bug(bug_id, comment_text) - else: - log(comment_text) - log("No bug id provided.") - - -class LandPatchesFromBugs(Command): - def __init__(self): - options = WebKitLandingScripts.cleaning_options() - options += WebKitLandingScripts.land_options() - Command.__init__(self, 'Lands all patches on a bug optionally testing them first', 'BUGID', options=options) - - @staticmethod - def handled_error(error): - log(error) - exit(2) # Exit 2 insted of 1 to indicate to the commit-queue to indicate we handled the error, and that the queue should keep looping. - - @classmethod - def land_patches(cls, bug_id, patches, options, tool): - try: - comment_text = "" - for patch in patches: - tool.scm().update_webkit() # Update before every patch in case the tree has changed - log("Applying %s from bug %s." % (patch['id'], bug_id)) - tool.scm().apply_patch(patch, force=options.commit_queue) - # Make sure the tree is still green after updating, before building this patch. - # The first patch ends up checking tree status twice, but that's OK. - WebKitLandingScripts.ensure_builders_are_green(tool.buildbot, options) - comment_text = WebKitLandingScripts.build_and_commit(tool.scm(), options) - tool.bugs.clear_attachment_flags(patch['id'], comment_text) - - if options.close_bug: - tool.bugs.close_bug_as_fixed(bug_id, "All reviewed patches have been landed. Closing bug.") - except CheckoutNeedsUpdate, e: - log("Commit was rejected because the checkout is out of date. Please update and try again.") - log("You can pass --no-build to skip building/testing after update if you believe the new commits did not affect the results.") - cls.handled_error(e) - except ScriptError, e: - # Mark the patch as commit-queue- and comment in the bug. - tool.bugs.reject_patch_from_commit_queue(patch['id'], e.message_with_output()) - cls.handled_error(e) - - @staticmethod - def _fetch_list_of_patches_to_land(options, args, tool): - bugs_to_patches = {} - patch_count = 0 - for bug_id in args: - patches = [] - if options.commit_queue: - patches = tool.bugs.fetch_commit_queue_patches_from_bug(bug_id, reject_invalid_patches=True) - else: - patches = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) - - patches_found = len(patches) - log("%s found on bug %s." % (pluralize("reviewed patch", patches_found), bug_id)) - - patch_count += patches_found - if patches_found: - bugs_to_patches[bug_id] = patches - - log("Landing %s from %s." % (pluralize("patch", patch_count), pluralize("bug", len(args)))) - return bugs_to_patches - - def execute(self, options, args, tool): - if not len(args): - error("bug-id(s) required") - - # Check the tree status here so we can fail early - WebKitLandingScripts.ensure_builders_are_green(tool.buildbot, options) - - bugs_to_patches = self._fetch_list_of_patches_to_land(options, args, tool) - - WebKitLandingScripts.setup_for_landing(tool.scm(), options) - - for bug_id in bugs_to_patches.keys(): - self.land_patches(bug_id, bugs_to_patches[bug_id], options, tool) - - -class CommitMessageForCurrentDiff(Command): - def __init__(self): - Command.__init__(self, 'Prints a commit message suitable for the uncommitted changes.') - - def execute(self, options, args, tool): - os.chdir(tool.scm().checkout_root) - print "%s" % commit_message_for_this_commit(tool.scm()).message() - - -class ObsoleteAttachmentsOnBug(Command): - def __init__(self): - Command.__init__(self, 'Marks all attachments on a bug as obsolete.', 'BUGID') - - def execute(self, options, args, tool): - bug_id = args[0] - attachments = tool.bugs.fetch_attachments_from_bug(bug_id) - for attachment in attachments: - if not attachment['is_obsolete']: - tool.bugs.obsolete_attachment(attachment['id']) - +from modules.buildsteps import BuildSteps +from modules.commands.download import * +from modules.commands.early_warning_system import * +from modules.commands.queries import * +from modules.commands.queues import * +from modules.commands.upload import * +from modules.executive import Executive +from modules.logging import log +from modules.multicommandtool import MultiCommandTool +from modules.scm import detect_scm_system + +class BugzillaTool(MultiCommandTool): + def __init__(self): + MultiCommandTool.__init__(self) + self.global_option_parser.add_option("--dry-run", action="callback", help="do not touch remote servers", callback=self.dry_run_callback) -class PostDiffAsPatchToBug(Command): - def __init__(self): - options = [ - make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: 'patch')"), - ] - options += self.posting_options() - Command.__init__(self, 'Attaches the current working directory diff to a bug as a patch file.', '[BUGID]', options=options) - - @staticmethod - def posting_options(): - return [ - make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one."), - make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), - make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), - ] - - @staticmethod - def obsolete_patches_on_bug(bug_id, bugs): - patches = bugs.fetch_patches_from_bug(bug_id) - if len(patches): - log("Obsoleting %s on bug %s" % (pluralize('old patch', len(patches)), bug_id)) - for patch in patches: - bugs.obsolete_attachment(patch['id']) - - def execute(self, options, args, tool): - # Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs). - bug_id = (args and args[0]) or parse_bug_id(tool.scm().create_patch()) - if not bug_id: - error("No bug id passed and no bug url found in diff, can't post.") - - if options.obsolete_patches: - self.obsolete_patches_on_bug(bug_id, tool.bugs) - - diff = tool.scm().create_patch() - diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object - - description = options.description or "Patch v1" - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - -class PostCommitsAsPatchesToBug(Command): - def __init__(self): - options = [ - make_option("-b", "--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."), - make_option("--add-log-as-comment", action="store_true", dest="add_log_as_comment", default=False, help="Add commit log message as a comment when uploading the patch."), - make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: description from commit message)"), - ] - options += PostDiffAsPatchToBug.posting_options() - Command.__init__(self, 'Attaches a range of local commits to bugs as patch files.', 'COMMITISH', options=options, requires_local_commits=True) - - def _comment_text_for_commit(self, options, commit_message, tool, commit_id): - comment_text = None - if (options.add_log_as_comment): - comment_text = commit_message.body(lstrip=True) - comment_text += "---\n" - comment_text += tool.scm().files_changed_summary_for_commit(commit_id) - return comment_text - - def _diff_file_for_commit(self, tool, commit_id): - diff = tool.scm().create_patch_from_local_commit(commit_id) - return StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object - - def execute(self, options, args, tool): - if not args: - error("%s argument is required" % self.argument_names) - - commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) - if len(commit_ids) > 10: # We could lower this limit, 10 is too many for one bug as-is. - error("bugzilla-tool does not support attaching %s at once. Are you sure you passed the right commit range?" % (pluralize('patch', len(commit_ids)))) - - have_obsoleted_patches = set() - for commit_id in commit_ids: - commit_message = tool.scm().commit_message_for_local_commit(commit_id) - - # Prefer --bug-id=, then a bug url in the commit message, then a bug url in the entire commit diff (i.e. ChangeLogs). - bug_id = options.bug_id or parse_bug_id(commit_message.message()) or parse_bug_id(tool.scm().create_patch_from_local_commit(commit_id)) - if not bug_id: - log("Skipping %s: No bug id found in commit or specified with --bug-id." % commit_id) - continue - - if options.obsolete_patches and bug_id not in have_obsoleted_patches: - PostDiffAsPatchToBug.obsolete_patches_on_bug(bug_id, tool.bugs) - have_obsoleted_patches.add(bug_id) - - diff_file = self._diff_file_for_commit(tool, commit_id) - description = options.description or commit_message.description(lstrip=True, strip_url=True) - comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id) - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - -class RolloutCommit(Command): - def __init__(self): - options = WebKitLandingScripts.land_options() - options += WebKitLandingScripts.cleaning_options() - options.append(make_option("--complete-rollout", action="store_true", dest="complete_rollout", help="Experimental support for complete unsupervised rollouts, including re-opening the bug. Not recommended.")) - Command.__init__(self, 'Reverts the given revision and commits the revert and re-opens the original bug.', 'REVISION [BUGID]', options=options) - - @staticmethod - def _create_changelogs_for_revert(scm, revision): - # First, discard the ChangeLog changes from the rollout. - changelog_paths = scm.modified_changelogs() - scm.revert_files(changelog_paths) - - # Second, make new ChangeLog entries for this rollout. - # This could move to prepare-ChangeLog by adding a --revert= option. - WebKitLandingScripts.run_webkit_script("prepare-ChangeLog") - for changelog_path in changelog_paths: - ChangeLog(changelog_path).update_for_revert(revision) - - @staticmethod - def _parse_bug_id_from_revision_diff(tool, revision): - original_diff = tool.scm().diff_for_revision(revision) - return parse_bug_id(original_diff) - - @staticmethod - def _reopen_bug_after_rollout(tool, bug_id, comment_text): - if bug_id: - tool.bugs.reopen_bug(bug_id, comment_text) - else: - log(comment_text) - log("No bugs were updated or re-opened to reflect this rollout.") - - def execute(self, options, args, tool): - if not args: - error("REVISION is required, see --help.") - revision = args[0] - 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.") - - WebKitLandingScripts.setup_for_landing(tool.scm(), options) - tool.scm().update_webkit() - tool.scm().apply_reverse_diff(revision) - self._create_changelogs_for_revert(tool.scm(), revision) - - # 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 options.complete_rollout: - log("\nNOTE: Rollout support is experimental.\nPlease verify the rollout diff and use 'bugzilla-tool land-diff %s' to commit the rollout." % bug_id) - else: - comment_text = WebKitLandingScripts.build_and_commit(tool.scm(), options) - self._reopen_bug_after_rollout(tool, bug_id, comment_text) - - -class CreateBug(Command): - def __init__(self): - options = [ - make_option("--cc", action="store", type="string", dest="cc", help="Comma-separated list of email addresses to carbon-copy."), - make_option("--component", action="store", type="string", dest="component", help="Component for the new bug."), - make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."), - make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), - make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), - ] - Command.__init__(self, 'Create a bug from local changes or local commits.', '[COMMITISH]', options=options) - - def create_bug_from_commit(self, options, args, tool): - commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) - if len(commit_ids) > 3: - error("Are you sure you want to create one bug with %s patches?" % len(commit_ids)) - - commit_id = commit_ids[0] - - bug_title = "" - comment_text = "" - if options.prompt: - (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() - else: - commit_message = tool.scm().commit_message_for_local_commit(commit_id) - bug_title = commit_message.description(lstrip=True, strip_url=True) - comment_text = commit_message.body(lstrip=True) - comment_text += "---\n" - comment_text += tool.scm().files_changed_summary_for_commit(commit_id) - - diff = tool.scm().create_patch_from_local_commit(commit_id) - diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - if bug_id and len(commit_ids) > 1: - options.bug_id = bug_id - options.obsolete_patches = False - # FIXME: We should pass through --no-comment switch as well. - PostCommitsAsPatchesToBug.execute(self, options, commit_ids[1:], tool) - - def create_bug_from_patch(self, options, args, tool): - bug_title = "" - comment_text = "" - if options.prompt: - (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() - else: - commit_message = commit_message_for_this_commit(tool.scm()) - bug_title = commit_message.description(lstrip=True, strip_url=True) - comment_text = commit_message.body(lstrip=True) - - diff = tool.scm().create_patch() - diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) - - def prompt_for_bug_title_and_comment(self): - bug_title = raw_input("Bug title: ") - print "Bug comment (hit ^D on blank line to end):" - lines = sys.stdin.readlines() - try: - sys.stdin.seek(0, os.SEEK_END) - except IOError: - # Cygwin raises an Illegal Seek (errno 29) exception when the above - # seek() call is made. Ignoring it seems to cause no harm. - # FIXME: Figure out a way to get avoid the exception in the first - # place. - pass - else: - raise - comment_text = ''.join(lines) - return (bug_title, comment_text) - - def execute(self, options, args, tool): - if len(args): - if (not tool.scm().supports_local_commits()): - error("Extra arguments not supported; patch is taken from working directory.") - self.create_bug_from_commit(options, args, tool) - else: - self.create_bug_from_patch(options, args, tool) - - -class CheckTreeStatus(Command): - def __init__(self): - Command.__init__(self, 'Print out the status of the webkit builders.') - - 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']) - - -class LandPatchesFromCommitQueue(Command): - def __init__(self): - options = [ - make_option("--is-relaunch", action="store_true", dest="is_relaunch", default=False, help="Internal: Used by the queue to indicate that it's relaunching itself."), - make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Do not ask the user for confirmation before running the queue. Dangerous!"), - make_option("--status-host", action="store", type="string", dest="status_host", default=StatusBot.default_host, help="Do not ask the user for confirmation before running the queue. Dangerous!"), - ] - Command.__init__(self, 'Run the commit queue.', options=options) - self._original_stdout = None - self._original_stderr = None - self._files_for_output = [] - - queue_log_path = 'commit_queue.log' - bug_logs_directory = 'commit_queue_logs' - - log_date_format = "%Y-%m-%d %H:%M:%S" - sleep_duration_text = "5 mins" - seconds_to_sleep = 300 - - def _tee_outputs_to_files(self, files): - if not self._original_stdout: - self._original_stdout = sys.stdout - self._original_stderr = sys.stderr - if files and len(files): - sys.stdout = tee(self._original_stdout, *files) - sys.stderr = tee(self._original_stderr, *files) - else: - sys.stdout = self._original_stdout - sys.stderr = self._original_stderr - - @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(self, message): - log(self._sleep_message(message)) - time.sleep(self.seconds_to_sleep) - self._next_patch() - - def _update_status_and_sleep(self, message): - status_message = self._sleep_message(message) - self.status_bot.update_status(status_message) - log(status_message) - time.sleep(self.seconds_to_sleep) - self._next_patch() - - def _next_patch(self): - # Re-exec this script to catch any updates to the script. - # Make sure that the re-execed commit-queue does not wait for the user. - args = sys.argv[:] - if args.count("--is-relaunch") == 0: - args.append("--is-relaunch") - os.execvp(sys.argv[0], args) - - @staticmethod - def _open_log_file(log_path): - (log_directory, log_name) = os.path.split(log_path) - if log_directory and not os.path.exists(log_directory): - os.makedirs(log_directory) - return open(log_path, 'a+') - - def _add_log_to_output_tee(self, path): - log_file = self._open_log_file(path) - self._files_for_output.append(log_file) - self._tee_outputs_to_files(self._files_for_output) - return log_file - - def _remove_log_from_output_tee(self, log_file): - self._files_for_output.remove(log_file) - self._tee_outputs_to_files(self._files_for_output) - log_file.close() - - def execute(self, options, args, tool): - if not options.is_relaunch: - log("CAUTION: commit-queue will discard all local changes in %s" % tool.scm().checkout_root) - if options.confirm: - response = raw_input("Are you sure? Type 'yes' to continue: ") - if (response != 'yes'): - error("User declined.") - - queue_log = self._add_log_to_output_tee(self.queue_log_path) - if not options.is_relaunch: - log("Running WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) - - self.status_bot = StatusBot(host=options.status_host) - - # Either of these calls could throw URLError which shouldn't stop the queue. - # We catch all exceptions just in case. - try: - # Fetch patches instead of just bug ids to that we validate reviewer/committer flags on every patch. - patches = tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) - if not len(patches): - self._update_status_and_sleep("Empty queue.") - patch_ids = map(lambda patch: patch['id'], patches) - first_bug_id = patches[0]['bug_id'] - log("%s in commit queue [%s]" % (pluralize('patch', len(patches)), ", ".join(patch_ids))) - - red_builders_names = tool.buildbot.red_core_builders_names() - if red_builders_names: - red_builders_names = map(lambda name: '"%s"' % name, red_builders_names) # Add quotes around the names. - self._update_status_and_sleep("Builders [%s] are red. See http://build.webkit.org." % ", ".join(red_builders_names)) - - self.status_bot.update_status("Landing patches from bug %s." % first_bug_id, bug_id=first_bug_id) - except Exception, e: - # Don't try tell the status bot, in case telling it causes an exception. - self._sleep("Exception while checking queue and bots: %s." % e) - - # Try to land patches on the first bug in the queue before looping - bug_log_path = os.path.join(self.bug_logs_directory, "%s.log" % first_bug_id) - bug_log = self._add_log_to_output_tee(bug_log_path) - bugzilla_tool_path = __file__ # re-execute this script - bugzilla_tool_args = [bugzilla_tool_path, 'land-patches', '--force-clean', '--commit-queue', '--quiet', first_bug_id] - try: - WebKitLandingScripts.run_and_throw_if_fail(bugzilla_tool_args) - except ScriptError, e: - # Unexpected failure! Mark the patch as commit-queue- and comment in the bug. - # exit(2) is a special exit code we use to indicate that the error was already handled by land-patches and we should keep looping anyway. - if e.exit_code != 2: - tool.bugs.reject_patch_from_commit_queue(patch['id'], "Unexpected failure when landing patch! Please file a bug against bugzilla-tool.\n%s" % e.message_with_output()) - self._remove_log_from_output_tee(bug_log) - # self._remove_log_from_output_tee(queue_log) # implicit in the exec() - self._next_patch() - - -class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter): - def __init__(self): - IndentedHelpFormatter.__init__(self) - - # The standard IndentedHelpFormatter paragraph-wraps the epilog, killing our custom formatting. - def format_epilog(self, epilog): - if epilog: - return "\n" + epilog + "\n" - return "" - - -class HelpPrintingOptionParser(OptionParser): - def error(self, msg): - self.print_usage(sys.stderr) - error_message = "%s: error: %s\n" % (self.get_prog_name(), msg) - error_message += "\nType '" + self.get_prog_name() + " --help' to see usage.\n" - self.exit(2, error_message) - - -class BugzillaTool: - def __init__(self): - self.cached_scm = None self.bugs = Bugzilla() self.buildbot = BuildBot() - self.commands = [ - { 'name' : 'bugs-to-commit', 'object' : BugsInCommitQueue() }, - { 'name' : 'patches-to-commit', 'object' : PatchesInCommitQueue() }, - { 'name' : 'reviewed-patches', 'object' : ReviewedPatchesOnBug() }, - { 'name' : 'create-bug', 'object' : CreateBug() }, - { 'name' : 'apply-patches', 'object' : ApplyPatchesFromBug() }, - { 'name' : 'land-diff', 'object' : LandAndUpdateBug() }, - { 'name' : 'land-patches', 'object' : LandPatchesFromBugs() }, - { 'name' : 'commit-message', 'object' : CommitMessageForCurrentDiff() }, - { 'name' : 'obsolete-attachments', 'object' : ObsoleteAttachmentsOnBug() }, - { 'name' : 'post-diff', 'object' : PostDiffAsPatchToBug() }, - { 'name' : 'post-commits', 'object' : PostCommitsAsPatchesToBug() }, - { 'name' : 'tree-status', 'object' : CheckTreeStatus() }, - { 'name' : 'commit-queue', 'object' : LandPatchesFromCommitQueue() }, - { 'name' : 'rollout', 'object' : RolloutCommit() }, - ] + self.executive = Executive() + self._scm = None + self._status = None + self.steps = BuildSteps() - self.global_option_parser = HelpPrintingOptionParser(usage=self.usage_line(), formatter=NonWrappingEpilogIndentedHelpFormatter(), epilog=self.commands_usage()) - self.global_option_parser.add_option("--dry-run", action="store_true", dest="dryrun", help="do not touch remote servers", default=False) + def dry_run_callback(self, option, opt, value, parser): + self.scm().dryrun = True + self.bugs.dryrun = True 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.cached_scm: - self.cached_scm = detect_scm_system(original_cwd) - - if not self.cached_scm: + 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.cached_scm = detect_scm_system(webkit_directory) - if self.cached_scm: + 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.cached_scm - - @staticmethod - def usage_line(): - return "Usage: %prog [options] command [command-options] [command-arguments]" - - def commands_usage(self): - commands_text = "Commands:\n" - longest_name_length = 0 - command_rows = [] - scm_supports_local_commits = self.scm().supports_local_commits() - for command in self.commands: - command_object = command['object'] - if command_object.requires_local_commits and not scm_supports_local_commits: - continue - command_name_and_args = command_object.name_with_arguments(command['name']) - command_rows.append({ 'name-and-args': command_name_and_args, 'object': command_object }) - longest_name_length = max([longest_name_length, len(command_name_and_args)]) - - # Use our own help formatter so as to indent enough. - formatter = IndentedHelpFormatter() - formatter.indent() - formatter.indent() - - for row in command_rows: - command_object = row['object'] - commands_text += " " + row['name-and-args'].ljust(longest_name_length + 3) + command_object.help_text + "\n" - commands_text += command_object.option_parser.format_option_help(formatter) - return commands_text - - def handle_global_args(self, args): - (options, args) = self.global_option_parser.parse_args(args) - if len(args): - # We'll never hit this because split_args splits at the first arg without a leading '-' - self.global_option_parser.error("Extra arguments before command: " + args) - - if options.dryrun: - self.scm().dryrun = True - self.bugs.dryrun = True - - @staticmethod - def split_args(args): - # Assume the first argument which doesn't start with '-' is the command name. - command_index = 0 - for arg in args: - if arg[0] != '-': - break - command_index += 1 - else: - return (args[:], None, []) - global_args = args[:command_index] - command = args[command_index] - command_args = args[command_index + 1:] - return (global_args, command, command_args) - - def command_by_name(self, command_name): - for command in self.commands: - if command_name == command['name']: - return command - return None - - def main(self): - (global_args, command_name, args_after_command_name) = self.split_args(sys.argv[1:]) - - # Handle --help, etc: - self.handle_global_args(global_args) - - if not command_name: - self.global_option_parser.error("No command specified") - - command = self.command_by_name(command_name) - if not command: - self.global_option_parser.error(command_name + " is not a recognized command") + return self._scm - command_object = command['object'] + def status(self): + if not self._status: + self._status = StatusBot() + return self._status - if command_object.requires_local_commits and not self.scm().supports_local_commits(): - error(command_name + " requires local commits using %s in %s." % (self.scm().display_name(), self.scm().checkout_root)) + def path(self): + return __file__ - (command_options, command_args) = command_object.parse_args(args_after_command_name) - return command_object.execute(command_options, command_args, self) + 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 + 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) -def main(): - tool = BugzillaTool() - return tool.main() if __name__ == "__main__": - main() + BugzillaTool().main() diff --git a/WebKitTools/Scripts/build-webkit b/WebKitTools/Scripts/build-webkit index 4f78eef..566965b 100755 --- a/WebKitTools/Scripts/build-webkit +++ b/WebKitTools/Scripts/build-webkit @@ -50,9 +50,9 @@ my $minimal = 0; my $makeArgs; my $startTime = time(); -my ($threeDCanvasSupport, $threeDRenderingSupport, $channelMessagingSupport, $databaseSupport, $datagridSupport, $domStorageSupport, - $eventsourceSupport, $filtersSupport, $geolocationSupport, $iconDatabaseSupport, - $javaScriptDebuggerSupport, $mathmlSupport, $offlineWebApplicationSupport, $rubySupport, $sharedWorkersSupport, +my ($threeDCanvasSupport, $threeDRenderingSupport, $channelMessagingSupport, $databaseSupport, $datagridSupport, $datalistSupport, + $domStorageSupport, $eventsourceSupport, $filtersSupport, $geolocationSupport, $iconDatabaseSupport, + $javaScriptDebuggerSupport, $mathmlSupport, $offlineWebApplicationSupport, $sharedWorkersSupport, $svgSupport, $svgAnimationSupport, $svgAsImageSupport, $svgDOMObjCBindingsSupport, $svgFontsSupport, $svgForeignObjectSupport, $svgUseSupport, $videoSupport, $webSocketsSupport, $wmlSupport, $wcssSupport, $xhtmlmpSupport, $workersSupport, $xpathSupport, $xsltSupport, $coverageSupport, $notificationsSupport); @@ -75,6 +75,9 @@ my @features = ( { option => "datagrid", desc => "Toggle Datagrid Support", define => "ENABLE_DATAGRID", default => 1, value => \$datagridSupport }, + + { option => "datalist", desc => "Toggle HTML5 datalist support", + define => "ENABLE_DATALIST", default => 1, value => \$datalistSupport }, { option => "dom-storage", desc => "Toggle DOM Storage Support", define => "ENABLE_DOM_STORAGE", default => 1, value => \$domStorageSupport }, @@ -83,7 +86,7 @@ my @features = ( define => "ENABLE_EVENTSOURCE", default => 1, value => \$eventsourceSupport }, { option => "filters", desc => "Toggle Filters support", - define => "ENABLE_FILTERS", default => 0, value => \$filtersSupport }, + define => "ENABLE_FILTERS", default => (isAppleWebKit() || isGtk() || isQt()), value => \$filtersSupport }, { option => "geolocation", desc => "Toggle Geolocation support", define => "ENABLE_GEOLOCATION", default => isGtk(), value => \$geolocationSupport }, @@ -103,9 +106,6 @@ my @features = ( { option => "offline-web-applications", desc => "Toggle Offline Web Application Support", define => "ENABLE_OFFLINE_WEB_APPLICATIONS", default => 1, value => \$offlineWebApplicationSupport }, - { option => "ruby", desc => "Toggle HTML5 Ruby support", - define => "ENABLE_RUBY", default => 1, value => \$rubySupport }, - { option => "shared-workers", desc => "Toggle SharedWorkers support", define => "ENABLE_SHARED_WORKERS", default => (isAppleWebKit() || isGtk()), value => \$sharedWorkersSupport }, @@ -313,6 +313,7 @@ removeLibraryDependingOnSVG("WebCore", $svgSupport); if (isWx()) { downloadWafIfNeeded(); + push @projects, 'WebKitTools/DumpRenderTree'; push @projects, 'WebKitTools/wx/browser'; push @projects, 'WebKit/wx/bindings/python'; } @@ -321,7 +322,7 @@ if (isChromium()) { # Chromium doesn't build by project directories. @projects = (); my $result = buildChromium($clean, @options); - exit $result if $result; + exit exitStatus($result) if exitStatus($result); } # Build, and abort if the build fails. @@ -361,7 +362,7 @@ for my $dir (@projects) { if (isAppleWinWebKit()) { print "\n\n===== BUILD FAILED ======\n\n"; my $scriptDir = relativeScriptsDir(); - print "Please ensure you have run $scriptDir/update-webkit to install depenedencies.\n\n"; + print "Please ensure you have run $scriptDir/update-webkit to install dependencies.\n\n"; my $baseProductDir = baseProductDir(); print "You can view build errors by checking the BuildLog.htm files located at:\n$baseProductDir/obj/<project>/<config>.\n"; } diff --git a/WebKitTools/Scripts/commit-log-editor b/WebKitTools/Scripts/commit-log-editor index e58b181..75017e3 100755 --- a/WebKitTools/Scripts/commit-log-editor +++ b/WebKitTools/Scripts/commit-log-editor @@ -170,7 +170,8 @@ for my $changeLog (@changeLogs) { # Attempt to insert the "patch by" line, after the first blank line. if ($previousLineWasBlank && $hasAuthorInfoToWrite && $lineCount > 0) { - my $authorAndCommitterAreSamePerson = $ENV{EMAIL_ADDRESS} && $email eq $ENV{EMAIL_ADDRESS}; + my $committerEmail = changeLogEmailAddress(); + my $authorAndCommitterAreSamePerson = $email eq $committerEmail; if (!$authorAndCommitterAreSamePerson) { $contents .= "Patch by $author <$email> on $date\n"; $hasAuthorInfoToWrite = 0; @@ -229,7 +230,7 @@ if (isGit() && scalar keys %changeLogSort == 0) { chomp($webkitGenerateCommitMessage = `git config --bool core.webkitGenerateCommitMessage`); } if ($webkitGenerateCommitMessage ne "false") { - open CHANGELOG_ENTRIES, "-|", "prepare-ChangeLog --git-index --no-write" or die "prepare-ChangeLog failed: $!.\n"; + open CHANGELOG_ENTRIES, "-|", "$FindBin::Bin/prepare-ChangeLog --git-index --no-write" or die "prepare-ChangeLog failed: $!.\n"; while (<CHANGELOG_ENTRIES>) { print NEWLOG normalizeLineEndings($_, $endl); } diff --git a/WebKitTools/Scripts/do-webcore-rename b/WebKitTools/Scripts/do-webcore-rename index 2d6ca46..a65fa4f 100755 --- a/WebKitTools/Scripts/do-webcore-rename +++ b/WebKitTools/Scripts/do-webcore-rename @@ -33,6 +33,7 @@ use FindBin; use lib $FindBin::Bin; use webkitdirs; use File::Find; +use VCSUtils; setConfiguration(); chdirWebKit(); @@ -66,9 +67,229 @@ sub wanted push @paths, $File::Find::name; } - +my $isDOMTypeRename = 1; my %renames = ( - "parseURL" => "deprecatedParseURL" + "CanvasActiveInfo" => "WebGLActiveInfo", + "canvasActiveInfo" => "webGLActiveInfo", + "CanvasActiveInfoConstructor" => "WebGLActiveInfoConstructor", + "CanvasActiveInfoPrototype" => "WebGLActiveInfoPrototype", + "toCanvasActiveInfo" => "toWebGLActiveInfo", + "JSCanvasActiveInfo" => "JSWebGLActiveInfo", + "JSCanvasActiveInfoPrototype" => "JSWebGLActiveInfoPrototype", + "JSCanvasActiveInfoConstructor" => "JSWebGLActiveInfoConstructor", + "JSCanvasActiveInfoCustom" => "JSWebGLActiveInfoCustom", + "V8CanvasActiveInfo" => "V8WebGLActiveInfo", + "V8CanvasActiveInfoPrototype" => "V8WebGLActiveInfoPrototype", + "V8CanvasActiveInfoConstructor" => "V8WebGLActiveInfoConstructor", + "V8CanvasActiveInfoCustom" => "V8WebGLActiveInfoCustom", + "CanvasArray" => "WebGLArray", + "canvasArray" => "webGLArray", + "CanvasArrayConstructor" => "WebGLArrayConstructor", + "CanvasArrayPrototype" => "WebGLArrayPrototype", + "toCanvasArray" => "toWebGLArray", + "JSCanvasArray" => "JSWebGLArray", + "JSCanvasArrayPrototype" => "JSWebGLArrayPrototype", + "JSCanvasArrayConstructor" => "JSWebGLArrayConstructor", + "JSCanvasArrayCustom" => "JSWebGLArrayCustom", + "V8CanvasArray" => "V8WebGLArray", + "V8CanvasArrayPrototype" => "V8WebGLArrayPrototype", + "V8CanvasArrayConstructor" => "V8WebGLArrayConstructor", + "V8CanvasArrayCustom" => "V8WebGLArrayCustom", + "CanvasArrayBuffer" => "WebGLArrayBuffer", + "canvasArrayBuffer" => "webGLArrayBuffer", + "CanvasArrayBufferConstructor" => "WebGLArrayBufferConstructor", + "CanvasArrayBufferPrototype" => "WebGLArrayBufferPrototype", + "toCanvasArrayBuffer" => "toWebGLArrayBuffer", + "JSCanvasArrayBuffer" => "JSWebGLArrayBuffer", + "JSCanvasArrayBufferPrototype" => "JSWebGLArrayBufferPrototype", + "JSCanvasArrayBufferConstructor" => "JSWebGLArrayBufferConstructor", + "JSCanvasArrayBufferCustom" => "JSWebGLArrayBufferCustom", + "V8CanvasArrayBuffer" => "V8WebGLArrayBuffer", + "V8CanvasArrayBufferPrototype" => "V8WebGLArrayBufferPrototype", + "V8CanvasArrayBufferConstructor" => "V8WebGLArrayBufferConstructor", + "V8CanvasArrayBufferCustom" => "V8WebGLArrayBufferCustom", + "CanvasBuffer" => "WebGLBuffer", + "canvasBuffer" => "webGLBuffer", + "CanvasBufferConstructor" => "WebGLBufferConstructor", + "CanvasBufferPrototype" => "WebGLBufferPrototype", + "toCanvasBuffer" => "toWebGLBuffer", + "JSCanvasBuffer" => "JSWebGLBuffer", + "JSCanvasBufferPrototype" => "JSWebGLBufferPrototype", + "JSCanvasBufferConstructor" => "JSWebGLBufferConstructor", + "JSCanvasBufferCustom" => "JSWebGLBufferCustom", + "V8CanvasBuffer" => "V8WebGLBuffer", + "V8CanvasBufferPrototype" => "V8WebGLBufferPrototype", + "V8CanvasBufferConstructor" => "V8WebGLBufferConstructor", + "V8CanvasBufferCustom" => "V8WebGLBufferCustom", + "CanvasByteArray" => "WebGLByteArray", + "canvasByteArray" => "webGLByteArray", + "CanvasByteArrayConstructor" => "WebGLByteArrayConstructor", + "CanvasByteArrayPrototype" => "WebGLByteArrayPrototype", + "toCanvasByteArray" => "toWebGLByteArray", + "JSCanvasByteArray" => "JSWebGLByteArray", + "JSCanvasByteArrayPrototype" => "JSWebGLByteArrayPrototype", + "JSCanvasByteArrayConstructor" => "JSWebGLByteArrayConstructor", + "JSCanvasByteArrayCustom" => "JSWebGLByteArrayCustom", + "V8CanvasByteArray" => "V8WebGLByteArray", + "V8CanvasByteArrayPrototype" => "V8WebGLByteArrayPrototype", + "V8CanvasByteArrayConstructor" => "V8WebGLByteArrayConstructor", + "V8CanvasByteArrayCustom" => "V8WebGLByteArrayCustom", + "CanvasFloatArray" => "WebGLFloatArray", + "canvasFloatArray" => "webGLFloatArray", + "CanvasFloatArrayConstructor" => "WebGLFloatArrayConstructor", + "CanvasFloatArrayPrototype" => "WebGLFloatArrayPrototype", + "toCanvasFloatArray" => "toWebGLFloatArray", + "JSCanvasFloatArray" => "JSWebGLFloatArray", + "JSCanvasFloatArrayPrototype" => "JSWebGLFloatArrayPrototype", + "JSCanvasFloatArrayConstructor" => "JSWebGLFloatArrayConstructor", + "JSCanvasFloatArrayCustom" => "JSWebGLFloatArrayCustom", + "V8CanvasFloatArray" => "V8WebGLFloatArray", + "V8CanvasFloatArrayPrototype" => "V8WebGLFloatArrayPrototype", + "V8CanvasFloatArrayConstructor" => "V8WebGLFloatArrayConstructor", + "V8CanvasFloatArrayCustom" => "V8WebGLFloatArrayCustom", + "CanvasFramebuffer" => "WebGLFramebuffer", + "canvasFramebuffer" => "webGLFramebuffer", + "CanvasFramebufferConstructor" => "WebGLFramebufferConstructor", + "CanvasFramebufferPrototype" => "WebGLFramebufferPrototype", + "toCanvasFramebuffer" => "toWebGLFramebuffer", + "JSCanvasFramebuffer" => "JSWebGLFramebuffer", + "JSCanvasFramebufferPrototype" => "JSWebGLFramebufferPrototype", + "JSCanvasFramebufferConstructor" => "JSWebGLFramebufferConstructor", + "JSCanvasFramebufferCustom" => "JSWebGLFramebufferCustom", + "V8CanvasFramebuffer" => "V8WebGLFramebuffer", + "V8CanvasFramebufferPrototype" => "V8WebGLFramebufferPrototype", + "V8CanvasFramebufferConstructor" => "V8WebGLFramebufferConstructor", + "V8CanvasFramebufferCustom" => "V8WebGLFramebufferCustom", + "CanvasIntArray" => "WebGLIntArray", + "canvasIntArray" => "webGLIntArray", + "CanvasIntArrayConstructor" => "WebGLIntArrayConstructor", + "CanvasIntArrayPrototype" => "WebGLIntArrayPrototype", + "toCanvasIntArray" => "toWebGLIntArray", + "JSCanvasIntArray" => "JSWebGLIntArray", + "JSCanvasIntArrayPrototype" => "JSWebGLIntArrayPrototype", + "JSCanvasIntArrayConstructor" => "JSWebGLIntArrayConstructor", + "JSCanvasIntArrayCustom" => "JSWebGLIntArrayCustom", + "V8CanvasIntArray" => "V8WebGLIntArray", + "V8CanvasIntArrayPrototype" => "V8WebGLIntArrayPrototype", + "V8CanvasIntArrayConstructor" => "V8WebGLIntArrayConstructor", + "V8CanvasIntArrayCustom" => "V8WebGLIntArrayCustom", + "CanvasProgram" => "WebGLProgram", + "canvasProgram" => "webGLProgram", + "CanvasProgramConstructor" => "WebGLProgramConstructor", + "CanvasProgramPrototype" => "WebGLProgramPrototype", + "toCanvasProgram" => "toWebGLProgram", + "JSCanvasProgram" => "JSWebGLProgram", + "JSCanvasProgramPrototype" => "JSWebGLProgramPrototype", + "JSCanvasProgramConstructor" => "JSWebGLProgramConstructor", + "JSCanvasProgramCustom" => "JSWebGLProgramCustom", + "V8CanvasProgram" => "V8WebGLProgram", + "V8CanvasProgramPrototype" => "V8WebGLProgramPrototype", + "V8CanvasProgramConstructor" => "V8WebGLProgramConstructor", + "V8CanvasProgramCustom" => "V8WebGLProgramCustom", + "CanvasRenderbuffer" => "WebGLRenderbuffer", + "canvasRenderbuffer" => "webGLRenderbuffer", + "CanvasRenderbufferConstructor" => "WebGLRenderbufferConstructor", + "CanvasRenderbufferPrototype" => "WebGLRenderbufferPrototype", + "toCanvasRenderbuffer" => "toWebGLRenderbuffer", + "JSCanvasRenderbuffer" => "JSWebGLRenderbuffer", + "JSCanvasRenderbufferPrototype" => "JSWebGLRenderbufferPrototype", + "JSCanvasRenderbufferConstructor" => "JSWebGLRenderbufferConstructor", + "JSCanvasRenderbufferCustom" => "JSWebGLRenderbufferCustom", + "V8CanvasRenderbuffer" => "V8WebGLRenderbuffer", + "V8CanvasRenderbufferPrototype" => "V8WebGLRenderbufferPrototype", + "V8CanvasRenderbufferConstructor" => "V8WebGLRenderbufferConstructor", + "V8CanvasRenderbufferCustom" => "V8WebGLRenderbufferCustom", + "CanvasRenderingContext3D" => "WebGLRenderingContext", + "canvasRenderingContext3D" => "webGLRenderingContext", + "CanvasRenderingContext3DConstructor" => "WebGLRenderingContextConstructor", + "CanvasRenderingContext3DPrototype" => "WebGLRenderingContextPrototype", + "toCanvasRenderingContext3D" => "toWebGLRenderingContext", + "JSCanvasRenderingContext3D" => "JSWebGLRenderingContext", + "JSCanvasRenderingContext3DPrototype" => "JSWebGLRenderingContextPrototype", + "JSCanvasRenderingContext3DConstructor" => "JSWebGLRenderingContextConstructor", + "JSCanvasRenderingContext3DCustom" => "JSWebGLRenderingContextCustom", + "V8CanvasRenderingContext3D" => "V8WebGLRenderingContext", + "V8CanvasRenderingContext3DPrototype" => "V8WebGLRenderingContextPrototype", + "V8CanvasRenderingContext3DConstructor" => "V8WebGLRenderingContextConstructor", + "V8CanvasRenderingContext3DCustom" => "V8WebGLRenderingContextCustom", + "CanvasShader" => "WebGLShader", + "canvasShader" => "webGLShader", + "CanvasShaderConstructor" => "WebGLShaderConstructor", + "CanvasShaderPrototype" => "WebGLShaderPrototype", + "toCanvasShader" => "toWebGLShader", + "JSCanvasShader" => "JSWebGLShader", + "JSCanvasShaderPrototype" => "JSWebGLShaderPrototype", + "JSCanvasShaderConstructor" => "JSWebGLShaderConstructor", + "JSCanvasShaderCustom" => "JSWebGLShaderCustom", + "V8CanvasShader" => "V8WebGLShader", + "V8CanvasShaderPrototype" => "V8WebGLShaderPrototype", + "V8CanvasShaderConstructor" => "V8WebGLShaderConstructor", + "V8CanvasShaderCustom" => "V8WebGLShaderCustom", + "CanvasShortArray" => "WebGLShortArray", + "canvasShortArray" => "webGLShortArray", + "CanvasShortArrayConstructor" => "WebGLShortArrayConstructor", + "CanvasShortArrayPrototype" => "WebGLShortArrayPrototype", + "toCanvasShortArray" => "toWebGLShortArray", + "JSCanvasShortArray" => "JSWebGLShortArray", + "JSCanvasShortArrayPrototype" => "JSWebGLShortArrayPrototype", + "JSCanvasShortArrayConstructor" => "JSWebGLShortArrayConstructor", + "JSCanvasShortArrayCustom" => "JSWebGLShortArrayCustom", + "V8CanvasShortArray" => "V8WebGLShortArray", + "V8CanvasShortArrayPrototype" => "V8WebGLShortArrayPrototype", + "V8CanvasShortArrayConstructor" => "V8WebGLShortArrayConstructor", + "V8CanvasShortArrayCustom" => "V8WebGLShortArrayCustom", + "CanvasTexture" => "WebGLTexture", + "canvasTexture" => "webGLTexture", + "CanvasTextureConstructor" => "WebGLTextureConstructor", + "CanvasTexturePrototype" => "WebGLTexturePrototype", + "toCanvasTexture" => "toWebGLTexture", + "JSCanvasTexture" => "JSWebGLTexture", + "JSCanvasTexturePrototype" => "JSWebGLTexturePrototype", + "JSCanvasTextureConstructor" => "JSWebGLTextureConstructor", + "JSCanvasTextureCustom" => "JSWebGLTextureCustom", + "V8CanvasTexture" => "V8WebGLTexture", + "V8CanvasTexturePrototype" => "V8WebGLTexturePrototype", + "V8CanvasTextureConstructor" => "V8WebGLTextureConstructor", + "V8CanvasTextureCustom" => "V8WebGLTextureCustom", + "CanvasUnsignedByteArray" => "WebGLUnsignedByteArray", + "canvasUnsignedByteArray" => "webGLUnsignedByteArray", + "CanvasUnsignedByteArrayConstructor" => "WebGLUnsignedByteArrayConstructor", + "CanvasUnsignedByteArrayPrototype" => "WebGLUnsignedByteArrayPrototype", + "toCanvasUnsignedByteArray" => "toWebGLUnsignedByteArray", + "JSCanvasUnsignedByteArray" => "JSWebGLUnsignedByteArray", + "JSCanvasUnsignedByteArrayPrototype" => "JSWebGLUnsignedByteArrayPrototype", + "JSCanvasUnsignedByteArrayConstructor" => "JSWebGLUnsignedByteArrayConstructor", + "JSCanvasUnsignedByteArrayCustom" => "JSWebGLUnsignedByteArrayCustom", + "V8CanvasUnsignedByteArray" => "V8WebGLUnsignedByteArray", + "V8CanvasUnsignedByteArrayPrototype" => "V8WebGLUnsignedByteArrayPrototype", + "V8CanvasUnsignedByteArrayConstructor" => "V8WebGLUnsignedByteArrayConstructor", + "V8CanvasUnsignedByteArrayCustom" => "V8WebGLUnsignedByteArrayCustom", + "CanvasUnsignedIntArray" => "WebGLUnsignedIntArray", + "canvasUnsignedIntArray" => "webGLUnsignedIntArray", + "CanvasUnsignedIntArrayConstructor" => "WebGLUnsignedIntArrayConstructor", + "CanvasUnsignedIntArrayPrototype" => "WebGLUnsignedIntArrayPrototype", + "toCanvasUnsignedIntArray" => "toWebGLUnsignedIntArray", + "JSCanvasUnsignedIntArray" => "JSWebGLUnsignedIntArray", + "JSCanvasUnsignedIntArrayPrototype" => "JSWebGLUnsignedIntArrayPrototype", + "JSCanvasUnsignedIntArrayConstructor" => "JSWebGLUnsignedIntArrayConstructor", + "JSCanvasUnsignedIntArrayCustom" => "JSWebGLUnsignedIntArrayCustom", + "V8CanvasUnsignedIntArray" => "V8WebGLUnsignedIntArray", + "V8CanvasUnsignedIntArrayPrototype" => "V8WebGLUnsignedIntArrayPrototype", + "V8CanvasUnsignedIntArrayConstructor" => "V8WebGLUnsignedIntArrayConstructor", + "V8CanvasUnsignedIntArrayCustom" => "V8WebGLUnsignedIntArrayCustom", + "CanvasUnsignedShortArray" => "WebGLUnsignedShortArray", + "canvasUnsignedShortArray" => "webGLUnsignedShortArray", + "CanvasUnsignedShortArrayConstructor" => "WebGLUnsignedShortArrayConstructor", + "CanvasUnsignedShortArrayPrototype" => "WebGLUnsignedShortArrayPrototype", + "toCanvasUnsignedShortArray" => "toWebGLUnsignedShortArray", + "JSCanvasUnsignedShortArray" => "JSWebGLUnsignedShortArray", + "JSCanvasUnsignedShortArrayPrototype" => "JSWebGLUnsignedShortArrayPrototype", + "JSCanvasUnsignedShortArrayConstructor" => "JSWebGLUnsignedShortArrayConstructor", + "JSCanvasUnsignedShortArrayCustom" => "JSWebGLUnsignedShortArrayCustom", + "V8CanvasUnsignedShortArray" => "V8WebGLUnsignedShortArray", + "V8CanvasUnsignedShortArrayPrototype" => "V8WebGLUnsignedShortArrayPrototype", + "V8CanvasUnsignedShortArrayConstructor" => "V8WebGLUnsignedShortArrayConstructor", + "V8CanvasUnsignedShortArrayCustom" => "V8WebGLUnsignedShortArrayCustom" ); my %renamesContemplatedForTheFuture = ( @@ -150,11 +371,18 @@ for my $file (sort @paths) { } } + +my $isGit = isGit(); + for my $file (sort @paths) { if ($newFile{$file}) { my $newFile = $newFile{$file}; print "Renaming $file to $newFile\n"; - system "svn move $file $newFile"; + if ($isGit) { + system "git mv $file $newFile"; + } else { + system "svn move $file $newFile"; + } } } @@ -171,8 +399,14 @@ for my $file (sort @paths) { } my $newContents = $contents; - for my $from (keys %renames) { - $newContents =~ s/\b$from(?!["\w])/$renames{$from}/g; # this " unconfuses Xcode syntax highlighting + if ($isDOMTypeRename) { + for my $from (keys %renames) { + $newContents =~ s/\b$from/$renames{$from}/g; + } + } else { + for my $from (keys %renames) { + $newContents =~ s/\b$from(?!["\w])/$renames{$from}/g; # this " unconfuses Xcode syntax highlighting + } } if ($newContents ne $contents) { diff --git a/WebKitTools/Scripts/modules/bugzilla.py b/WebKitTools/Scripts/modules/bugzilla.py index fe81b48..be78544 100644 --- a/WebKitTools/Scripts/modules/bugzilla.py +++ b/WebKitTools/Scripts/modules/bugzilla.py @@ -43,7 +43,7 @@ from modules.committers import CommitterList # WebKit includes a built copy of BeautifulSoup in Scripts/modules # so this import should always succeed. -from .BeautifulSoup import BeautifulSoup +from .BeautifulSoup import BeautifulSoup, SoupStrainer try: from mechanize import Browser @@ -95,10 +95,10 @@ def is_mac_os_x(): def parse_bug_id(message): match = re.search("http\://webkit\.org/b/(?P<bug_id>\d+)", message) if match: - return match.group('bug_id') + return int(match.group('bug_id')) match = re.search(Bugzilla.bug_server_regex + "show_bug\.cgi\?id=(?P<bug_id>\d+)", message) if match: - return match.group('bug_id') + return int(match.group('bug_id')) return None # FIXME: This should not depend on git for config storage @@ -163,17 +163,20 @@ class Bugzilla: def _parse_attachment_flag(self, element, flag_name, attachment, result_key): flag = element.find('flag', attrs={'name' : flag_name}) - if flag and flag['status'] == '+': - attachment[result_key] = flag['setter'] + if flag: + attachment[flag_name] = flag['status'] + if flag['status'] == '+': + attachment[result_key] = flag['setter'] def _parse_attachment_element(self, element, bug_id): attachment = {} attachment['bug_id'] = bug_id attachment['is_obsolete'] = (element.has_key('isobsolete') and element['isobsolete'] == "1") attachment['is_patch'] = (element.has_key('ispatch') and element['ispatch'] == "1") - attachment['id'] = str(element.find('attachid').string) + attachment['id'] = int(element.find('attachid').string) attachment['url'] = self.attachment_url_for_id(attachment['id']) attachment['name'] = unicode(element.find('desc').string) + attachment['attacher_email'] = str(element.find('attacher').string) attachment['type'] = str(element.find('type').string) self._parse_attachment_flag(element, 'review', attachment, 'reviewer_email') self._parse_attachment_flag(element, 'commit-queue', attachment, 'committer_email') @@ -192,6 +195,36 @@ class Bugzilla: attachments.append(attachment) return attachments + def _parse_bug_id_from_attachment_page(self, page): + up_link = BeautifulSoup(page).find('link', rel='Up') # The "Up" relation happens to point to the bug. + if not up_link: + return None # This attachment does not exist (or you don't have permissions to view it). + match = re.search("show_bug.cgi\?id=(?P<bug_id>\d+)", up_link['href']) + return int(match.group('bug_id')) + + def bug_id_for_attachment_id(self, attachment_id): + attachment_url = self.attachment_url_for_id(attachment_id, 'edit') + log("Fetching: %s" % attachment_url) + page = urllib2.urlopen(attachment_url) + return self._parse_bug_id_from_attachment_page(page) + + # This should really return an Attachment object + # which can lazily fetch any missing data. + def fetch_attachment(self, attachment_id): + # We could grab all the attachment details off of the attachment edit page + # but we already have working code to do so off of the bugs page, so re-use that. + bug_id = self.bug_id_for_attachment_id(attachment_id) + if not bug_id: + return None + attachments = self.fetch_attachments_from_bug(bug_id) + for attachment in attachments: + # FIXME: Once we have a real Attachment class we shouldn't paper over this possible comparison failure + # and we should remove the int() == int() hacks and leave it just ==. + if int(attachment['id']) == int(attachment_id): + self._validate_committer_and_reviewer(attachment) + return attachment + return None # This should never be hit. + def fetch_title_from_bug(self, bug_id): bug_url = self.bug_url_for_bug_id(bug_id, xml=True) page = urllib2.urlopen(bug_url) @@ -209,6 +242,14 @@ class Bugzilla: def _view_source_link(self, local_path): return "http://trac.webkit.org/browser/trunk/%s" % local_path + def _flag_permission_rejection_message(self, setter_email, flag_name): + committer_list = "WebKitTools/Scripts/modules/committers.py" + contribution_guidlines_url = "http://webkit.org/coding/contributing.html" + rejection_message = "%s does not have %s permissions according to %s." % (setter_email, flag_name, self._view_source_link(committer_list)) + rejection_message += "\n\n- If you have %s rights please correct the error in %s by adding yourself to the file (no review needed) and then set the %s flag again." % (flag_name, committer_list, flag_name) + rejection_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_url) + return rejection_message + def _validate_setter_email(self, patch, result_key, lookup_function, rejection_function, reject_invalid_patches): setter_email = patch.get(result_key + '_email') if not setter_email: @@ -220,18 +261,30 @@ class Bugzilla: return patch[result_key] if reject_invalid_patches: - committer_list = "WebKitTools/Scripts/modules/committers.py" - failure_message = "%s does not have %s permissions according to %s." % (setter_email, result_key, self._view_source_link(committer_list)) - rejection_function(patch['id'], failure_message) + rejection_function(patch['id'], self._flag_permission_rejection_message(setter_email, result_key)) else: - log("Warning, attachment %s on bug %s has invalid %s (%s)", (patch['id'], patch['bug_id'], result_key, setter_email)) + log("Warning, attachment %s on bug %s has invalid %s (%s)" % (patch['id'], patch['bug_id'], result_key, setter_email)) return None def _validate_reviewer(self, patch, reject_invalid_patches): - return self._validate_setter_email(patch, 'reviewer', self.committers.reviewer_by_bugzilla_email, self.reject_patch_from_review_queue, reject_invalid_patches) + return self._validate_setter_email(patch, 'reviewer', self.committers.reviewer_by_email, self.reject_patch_from_review_queue, reject_invalid_patches) def _validate_committer(self, patch, reject_invalid_patches): - return self._validate_setter_email(patch, 'committer', self.committers.committer_by_bugzilla_email, self.reject_patch_from_commit_queue, reject_invalid_patches) + return self._validate_setter_email(patch, 'committer', self.committers.committer_by_email, self.reject_patch_from_commit_queue, reject_invalid_patches) + + # FIXME: This is a hack until we have a real Attachment object. + # _validate_committer and _validate_reviewer fill in the 'reviewer' and 'committer' + # keys which other parts of the code expect to be filled in. + def _validate_committer_and_reviewer(self, patch): + self._validate_reviewer(patch, reject_invalid_patches=False) + self._validate_committer(patch, reject_invalid_patches=False) + + def fetch_unreviewed_patches_from_bug(self, bug_id): + unreviewed_patches = [] + for attachment in self.fetch_attachments_from_bug(bug_id): + if attachment.get('review') == '?' and not attachment['is_obsolete']: + unreviewed_patches.append(attachment) + return unreviewed_patches def fetch_reviewed_patches_from_bug(self, bug_id, reject_invalid_patches=False): reviewed_patches = [] @@ -247,20 +300,44 @@ class Bugzilla: commit_queue_patches.append(attachment) return commit_queue_patches - def fetch_bug_ids_from_commit_queue(self): - commit_queue_url = self.bug_server_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=commit-queue%2B" - - page = urllib2.urlopen(commit_queue_url) + def _fetch_bug_ids_advanced_query(self, query): + page = urllib2.urlopen(query) soup = BeautifulSoup(page) bug_ids = [] # Grab the cells in the first column (which happens to be the bug ids) for bug_link_cell in soup('td', "first-child"): # tds with the class "first-child" bug_link = bug_link_cell.find("a") - bug_ids.append(bug_link.string) # the contents happen to be the bug id + bug_ids.append(int(bug_link.string)) # the contents happen to be the bug id return bug_ids + def _parse_attachment_ids_request_query(self, page): + digits = re.compile("\d+") + attachment_href = re.compile("attachment.cgi\?id=\d+&action=review") + attachment_links = SoupStrainer("a", href=attachment_href) + return [int(digits.search(tag["href"]).group(0)) for tag in BeautifulSoup(page, parseOnlyThese=attachment_links)] + + def _fetch_attachment_ids_request_query(self, query): + return self._parse_attachment_ids_request_query(urllib2.urlopen(query)) + + def fetch_bug_ids_from_commit_queue(self): + commit_queue_url = self.bug_server_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=commit-queue%2B" + return self._fetch_bug_ids_advanced_query(commit_queue_url) + + # List of all r+'d bugs. + def fetch_bug_ids_from_needs_commit_list(self): + needs_commit_query_url = self.bug_server_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" + return self._fetch_bug_ids_advanced_query(needs_commit_query_url) + + def fetch_bug_ids_from_review_queue(self): + review_queue_url = self.bug_server_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?" + return self._fetch_bug_ids_advanced_query(review_queue_url) + + def fetch_attachment_ids_from_review_queue(self): + review_queue_url = self.bug_server_url + "request.cgi?action=queue&type=review&group=type" + return self._fetch_attachment_ids_request_query(review_queue_url) + def fetch_patches_from_commit_queue(self, reject_invalid_patches=False): patches_to_land = [] for bug_id in self.fetch_bug_ids_from_commit_queue(): @@ -268,6 +345,22 @@ class Bugzilla: patches_to_land += patches return patches_to_land + def fetch_patches_from_pending_commit_list(self): + patches_needing_commit = [] + for bug_id in self.fetch_bug_ids_from_needs_commit_list(): + patches = self.fetch_reviewed_patches_from_bug(bug_id) + patches_needing_commit += patches + return patches_needing_commit + + def fetch_patches_from_review_queue(self, limit=None): + patches_to_review = [] + for bug_id in self.fetch_bug_ids_from_review_queue(): + if limit and len(patches_to_review) >= limit: + break + patches = self.fetch_unreviewed_patches_from_bug(bug_id) + patches_to_review += patches + return patches_to_review + def authenticate(self): if self.authenticated: return @@ -312,7 +405,7 @@ class Bugzilla: if self.dryrun: log(comment_text) return - + self.browser.open("%sattachment.cgi?action=enter&bugid=%s" % (self.bug_server_url, bug_id)) self.browser.select_form(name="entryform") self._fill_attachment_form(description, patch_file_object, mark_for_review=mark_for_review, mark_for_commit_queue=mark_for_commit_queue, bug_id=bug_id) @@ -442,8 +535,20 @@ class Bugzilla: # Bugzilla has two textareas named 'comment', one is somehow hidden. We want the first. self.browser.set_value(comment_text, name='comment', nr=0) self.browser.submit() - - def post_comment_to_bug(self, bug_id, comment_text): + + def add_cc_to_bug(self, bug_id, email_address): + self.authenticate() + + log("Adding %s to the CC list for bug %s" % (email_address, bug_id)) + if self.dryrun: + return + + self.browser.open(self.bug_url_for_bug_id(bug_id)) + self.browser.select_form(name="changeform") + self.browser["newcc"] = email_address + self.browser.submit() + + def post_comment_to_bug(self, bug_id, comment_text, cc=None): self.authenticate() log("Adding comment to bug %s" % bug_id) @@ -453,7 +558,9 @@ class Bugzilla: self.browser.open(self.bug_url_for_bug_id(bug_id)) self.browser.select_form(name="changeform") - self.browser['comment'] = comment_text + self.browser["comment"] = comment_text + if cc: + self.browser["newcc"] = cc self.browser.submit() def close_bug_as_fixed(self, bug_id, comment_text=None): diff --git a/WebKitTools/Scripts/modules/bugzilla_unittest.py b/WebKitTools/Scripts/modules/bugzilla_unittest.py index f08031e..fb7f8c4 100644 --- a/WebKitTools/Scripts/modules/bugzilla_unittest.py +++ b/WebKitTools/Scripts/modules/bugzilla_unittest.py @@ -29,7 +29,7 @@ import unittest from modules.committers import CommitterList, Reviewer, Committer -from modules.bugzilla import Bugzilla +from modules.bugzilla import Bugzilla, parse_bug_id from modules.BeautifulSoup import BeautifulSoup @@ -61,17 +61,34 @@ class BugzillaTest(unittest.TestCase): </attachment> ''' _expected_example_attachment_parsing = { - 'bug_id' : "100", + 'bug_id' : 100, 'is_obsolete' : True, 'is_patch' : True, - 'id' : "33721", + 'id' : 33721, 'url' : "https://bugs.webkit.org/attachment.cgi?id=33721", 'name' : "Fixed whitespace issue", 'type' : "text/plain", + 'review' : '+', 'reviewer_email' : 'one@test.com', - 'committer_email' : 'two@test.com' + 'commit-queue' : '+', + 'committer_email' : 'two@test.com', + 'attacher_email' : 'christian.plesner.hansen@gmail.com', } + def test_parse_bug_id(self): + # FIXME: These would be all better as doctests + bugs = Bugzilla() + self.assertEquals(12345, parse_bug_id("http://webkit.org/b/12345")) + self.assertEquals(12345, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?id=12345")) + self.assertEquals(12345, parse_bug_id(bugs.short_bug_url_for_bug_id(12345))) + self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345))) + self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345, xml=True))) + + # Our bug parser is super-fragile, but at least we're testing it. + self.assertEquals(None, parse_bug_id("http://www.webkit.org/b/12345")) + self.assertEquals(None, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?ctype=xml&id=12345")) + + def test_attachment_parsing(self): bugzilla = Bugzilla() @@ -86,5 +103,71 @@ class BugzillaTest(unittest.TestCase): for key, expected_value in self._expected_example_attachment_parsing.items(): self.assertEquals(attachment[key], expected_value, ("Failure for key: %s: Actual='%s' Expected='%s'" % (key, attachment[key], expected_value))) + _sample_attachment_detail_page = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> + <head> + <title> + Attachment 41073 Details for Bug 27314</title> +<link rel="Top" href="https://bugs.webkit.org/"> + <link rel="Up" href="show_bug.cgi?id=27314"> +""" + + def test_attachment_detail_bug_parsing(self): + bugzilla = Bugzilla() + self.assertEquals(27314, bugzilla._parse_bug_id_from_attachment_page(self._sample_attachment_detail_page)) + + _sample_request_page = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> + <head> + <title>Request Queue</title> + </head> +<body> + +<h3>Flag: review</h3> + <table class="requests" cellspacing="0" cellpadding="4" border="1"> + <tr> + <th>Requester</th> + <th>Requestee</th> + <th>Bug</th> + <th>Attachment</th> + <th>Created</th> + </tr> + <tr> + <td>Shinichiro Hamaji <hamaji@chromium.org></td> + <td></td> + <td><a href="show_bug.cgi?id=30015">30015: text-transform:capitalize is failing in CSS2.1 test suite</a></td> + <td><a href="attachment.cgi?id=40511&action=review"> +40511: Patch v0</a></td> + <td>2009-10-02 04:58 PST</td> + </tr> + <tr> + <td>Zan Dobersek <zandobersek@gmail.com></td> + <td></td> + <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> + <td><a href="attachment.cgi?id=40722&action=review"> +40722: Media controls, the simple approach</a></td> + <td>2009-10-06 09:13 PST</td> + </tr> + <tr> + <td>Zan Dobersek <zandobersek@gmail.com></td> + <td></td> + <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> + <td><a href="attachment.cgi?id=40723&action=review"> +40723: Adjust the media slider thumb size</a></td> + <td>2009-10-06 09:15 PST</td> + </tr> + </table> +</body> +</html> +""" + + def test_request_page_parsing(self): + bugzilla = Bugzilla() + self.assertEquals([40511, 40722, 40723], bugzilla._parse_attachment_ids_request_query(self._sample_request_page)) + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/modules/buildbot.py b/WebKitTools/Scripts/modules/buildbot.py index e948d8c..548cad8 100644 --- a/WebKitTools/Scripts/modules/buildbot.py +++ b/WebKitTools/Scripts/modules/buildbot.py @@ -39,7 +39,8 @@ from modules.logging import log from .BeautifulSoup import BeautifulSoup class BuildBot: - def __init__(self, host="build.webkit.org"): + default_host = "build.webkit.org" + def __init__(self, host=default_host): self.buildbot_host = host self.buildbot_server_url = "http://%s/" % self.buildbot_host diff --git a/WebKitTools/Scripts/modules/buildsteps.py b/WebKitTools/Scripts/modules/buildsteps.py new file mode 100644 index 0000000..425b912 --- /dev/null +++ b/WebKitTools/Scripts/modules/buildsteps.py @@ -0,0 +1,254 @@ +# 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 optparse import make_option + +from modules.comments import bug_comment_from_commit_text +from modules.logging import log, error +from modules.webkitport import WebKitPort + + +class CommandOptions(object): + 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)") + 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") + 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.") + quiet = make_option("--quiet", action="store_true", dest="quiet", default=False, help="Produce less console output.") + non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.") + parent_command = make_option("--parent-command", action="store", dest="parent_command", default=None, help="(Internal) The command that spawned this instance.") + update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.") + build = make_option("--no-build", action="store_false", dest="build", default=True, help="Commit without building first, implies --no-test.") + test = make_option("--no-test", action="store_false", dest="test", default=True, help="Commit without running run-webkit-tests.") + close_bug = make_option("--no-close", action="store_false", dest="close_bug", default=True, help="Leave bug open after landing.") + port = make_option("--port", action="store", dest="port", default=None, help="Specify a port (e.g., mac, qt, gtk, ...).") + + +class AbstractStep(object): + def __init__(self, tool, options, patch=None): + self._tool = tool + self._options = options + self._patch = patch + self._port = None + + def _run_script(self, script_name, quiet=False, port=WebKitPort): + log("Running %s" % script_name) + self._tool.executive.run_and_throw_if_fail(port.script_path(script_name), quiet) + + # FIXME: The port should live on the tool. + def port(self): + if self._port: + return self._port + self._port = WebKitPort.port(self._options.port) + return self._port + + @classmethod + def options(cls): + return [] + + def run(self, tool): + raise NotImplementedError, "subclasses must implement" + + +class PrepareChangelogStep(AbstractStep): + def run(self): + self._run_script("prepare-ChangeLog") + + +class CleanWorkingDirectoryStep(AbstractStep): + def __init__(self, tool, options, patch=None, allow_local_commits=False): + AbstractStep.__init__(self, tool, options, patch) + self._allow_local_commits = allow_local_commits + + @classmethod + def options(cls): + return [ + CommandOptions.force_clean, + CommandOptions.clean, + ] + + def run(self): + os.chdir(self._tool.scm().checkout_root) + if not self._allow_local_commits: + self._tool.scm().ensure_no_local_commits(self._options.force_clean) + if self._options.clean: + self._tool.scm().ensure_clean_working_directory(force_clean=self._options.force_clean) + + +class UpdateStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.update, + CommandOptions.port, + ] + + def run(self): + if not self._options.update: + return + log("Updating working directory") + self._tool.executive.run_and_throw_if_fail(self.port().update_webkit_command()) + + +class ApplyPatchStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.non_interactive, + ] + + def run(self): + log("Processing patch %s from bug %s." % (self._patch["id"], self._patch["bug_id"])) + self._tool.scm().apply_patch(self._patch, force=self._options.non_interactive) + + +class EnsureBuildersAreGreenStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.check_builders, + ] + + def run(self): + if not self._options.check_builders: + return + red_builders_names = self._tool.buildbot.red_core_builders_names() + if not red_builders_names: + return + red_builders_names = map(lambda name: "\"%s\"" % name, red_builders_names) # Add quotes around the names. + error("Builders [%s] are red, please do not commit.\nSee http://%s.\nPass --ignore-builders to bypass this check." % (", ".join(red_builders_names), self._tool.buildbot.buildbot_host)) + + +class BuildStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.build, + CommandOptions.quiet, + ] + + def run(self): + if not self._options.build: + return + log("Building WebKit") + self._tool.executive.run_and_throw_if_fail(self.port().build_webkit_command(), self._options.quiet) + + +class CheckStyleStep(AbstractStep): + def run(self): + self._run_script("check-webkit-style") + + +class RunTestsStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.build, + CommandOptions.test, + CommandOptions.non_interactive, + CommandOptions.quiet, + CommandOptions.port, + ] + + def run(self): + if not self._options.build: + return + if not self._options.test: + return + args = self.port().run_webkit_tests_command() + if self._options.non_interactive: + args.append("--no-launch-safari") + args.append("--exit-after-n-failures=1") + if self._options.quiet: + args.append("--quiet") + self._tool.executive.run_and_throw_if_fail(args) + + +class CommitStep(AbstractStep): + def run(self): + commit_message = self._tool.scm().commit_message_for_this_commit() + return self._tool.scm().commit_with_message(commit_message.message()) + + +class ClosePatchStep(AbstractStep): + def run(self, commit_log): + comment_text = bug_comment_from_commit_text(self._tool.scm(), commit_log) + self._tool.bugs.clear_attachment_flags(self._patch["id"], comment_text) + + +class CloseBugStep(AbstractStep): + @classmethod + def options(cls): + return [ + CommandOptions.close_bug, + ] + + def run(self): + if not self._options.close_bug: + return + # Check to make sure there are no r? or r+ patches on the bug before closing. + # Assume that r- patches are just previous patches someone forgot to obsolete. + patches = self._tool.bugs.fetch_patches_from_bug(self._patch["bug_id"]) + for patch in patches: + review_flag = patch.get("review") + if review_flag == "?" or review_flag == "+": + log("Not closing bug %s as attachment %s has review=%s. Assuming there are more patches to land from this bug." % (patch["bug_id"], patch["id"], review_flag)) + return + self._tool.bugs.close_bug_as_fixed(self._patch["bug_id"], "All reviewed patches have been landed. Closing bug.") + + +# FIXME: This class is a dinosaur and should be extinct soon. +class BuildSteps: + # FIXME: The options should really live on each "Step" object. + @staticmethod + def cleaning_options(): + return [ + CommandOptions.force_clean, + CommandOptions.clean, + ] + + # FIXME: These distinctions are bogus. We need a better model for handling options. + @staticmethod + def build_options(): + return [ + CommandOptions.check_builders, + CommandOptions.quiet, + CommandOptions.non_interactive, + CommandOptions.parent_command, + CommandOptions.port, + ] + + @staticmethod + def land_options(): + return [ + CommandOptions.update, + CommandOptions.build, + CommandOptions.test, + CommandOptions.close_bug, + ] + diff --git a/WebKitTools/Scripts/modules/commands/__init__.py b/WebKitTools/Scripts/modules/commands/__init__.py new file mode 100644 index 0000000..ef65bee --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/__init__.py @@ -0,0 +1 @@ +# Required for Python to search this directory for module files diff --git a/WebKitTools/Scripts/modules/commands/commandtest.py b/WebKitTools/Scripts/modules/commands/commandtest.py new file mode 100644 index 0000000..618a517 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/commandtest.py @@ -0,0 +1,42 @@ +# 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 modules.mock import Mock +from modules.mock_bugzillatool import MockBugzillaTool +from modules.outputcapture import OutputCapture + +class CommandsTest(unittest.TestCase): + def assert_execute_outputs(self, command, args, expected_stdout="", expected_stderr="", options=Mock(), tool=MockBugzillaTool()): + capture = OutputCapture() + capture.capture_output() + command.execute(options, args, tool) + (stdout_string, stderr_string) = capture.restore_output() + self.assertEqual(stdout_string, expected_stdout) + self.assertEqual(expected_stderr, expected_stderr) diff --git a/WebKitTools/Scripts/modules/commands/download.py b/WebKitTools/Scripts/modules/commands/download.py new file mode 100644 index 0000000..2acd69f --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/download.py @@ -0,0 +1,370 @@ +#!/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. + +import os + +from optparse import make_option + +from modules.bugzilla import parse_bug_id +from modules.buildsteps import CommandOptions, BuildSteps, EnsureBuildersAreGreenStep, CleanWorkingDirectoryStep, UpdateStep, ApplyPatchStep, BuildStep, CheckStyleStep, PrepareChangelogStep +from modules.changelogs import ChangeLog +from modules.comments import bug_comment_from_commit_text +from modules.executive import ScriptError +from modules.grammar import pluralize +from modules.landingsequence import LandingSequence +from modules.logging import error, log +from modules.multicommandtool import Command +from modules.stepsequence import StepSequence + + +class Build(Command): + name = "build" + show_in_main_help = False + def __init__(self): + self._sequence = StepSequence([ + CleanWorkingDirectoryStep, + UpdateStep, + BuildStep + ]) + Command.__init__(self, "Update working copy and build", "", self._sequence.options()) + + def execute(self, options, args, tool): + self._sequence.run_and_handle_errors(tool, options) + + +class ApplyAttachment(Command): + name = "apply-attachment" + show_in_main_help = True + def __init__(self): + options = WebKitApplyingScripts.apply_options() + options += BuildSteps.cleaning_options() + Command.__init__(self, "Apply an attachment to the local working directory", "ATTACHMENT_ID", options=options) + + def execute(self, options, args, tool): + WebKitApplyingScripts.setup_for_patch_apply(tool, options) + attachment_id = args[0] + attachment = tool.bugs.fetch_attachment(attachment_id) + WebKitApplyingScripts.apply_patches_with_options(tool.scm(), [attachment], options) + + +class ApplyPatches(Command): + name = "apply-patches" + show_in_main_help = True + def __init__(self): + options = WebKitApplyingScripts.apply_options() + options += BuildSteps.cleaning_options() + Command.__init__(self, "Apply reviewed patches from provided bugs to the local working directory", "BUGID", options=options) + + def execute(self, options, args, tool): + WebKitApplyingScripts.setup_for_patch_apply(tool, options) + bug_id = args[0] + patches = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) + WebKitApplyingScripts.apply_patches_with_options(tool.scm(), patches, options) + + +class WebKitApplyingScripts: + @staticmethod + def apply_options(): + return [ + make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory before applying patches"), + make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch"), + CommandOptions.port, + ] + + @staticmethod + def setup_for_patch_apply(tool, options): + clean_step = CleanWorkingDirectoryStep(tool, options, allow_local_commits=True) + clean_step.run() + update_step = UpdateStep(tool, options) + update_step.run() + + @staticmethod + def apply_patches_with_options(scm, patches, options): + if options.local_commit and not scm.supports_local_commits(): + error("--local-commit passed, but %s does not support local commits" % scm.display_name()) + + for patch in patches: + log("Applying attachment %s from bug %s" % (patch["id"], patch["bug_id"])) + scm.apply_patch(patch) + if options.local_commit: + commit_message = scm.commit_message_for_this_commit() + scm.commit_locally_with_message(commit_message.message() or patch["name"]) + + +class LandDiffSequence(LandingSequence): + def run(self): + self.check_builders() + self.build() + self.test() + commit_log = self.commit() + self.close_bug(commit_log) + + def close_bug(self, commit_log): + comment_test = bug_comment_from_commit_text(self._tool.scm(), commit_log) + bug_id = self._patch["bug_id"] + if bug_id: + log("Updating bug %s" % bug_id) + if self._options.close_bug: + self._tool.bugs.close_bug_as_fixed(bug_id, comment_test) + else: + # FIXME: We should a smart way to figure out if the patch is attached + # to the bug, and if so obsolete it. + self._tool.bugs.post_comment_to_bug(bug_id, comment_test) + else: + log(comment_test) + log("No bug id provided.") + + +class LandDiff(Command): + name = "land-diff" + show_in_main_help = True + def __init__(self): + options = [ + make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER."), + ] + options += BuildSteps.build_options() + options += BuildSteps.land_options() + Command.__init__(self, "Land the current working directory diff and updates the associated bug if any", "[BUGID]", options=options) + + def guess_reviewer_from_bug(self, bugs, bug_id): + patches = bugs.fetch_reviewed_patches_from_bug(bug_id) + if len(patches) != 1: + log("%s on bug %s, cannot infer reviewer." % (pluralize("reviewed patch", len(patches)), bug_id)) + return None + patch = patches[0] + reviewer = patch["reviewer"] + log("Guessing \"%s\" as reviewer from attachment %s on bug %s." % (reviewer, patch["id"], bug_id)) + return reviewer + + def update_changelogs_with_reviewer(self, reviewer, bug_id, tool): + if not reviewer: + if not bug_id: + log("No bug id provided and --reviewer= not provided. Not updating ChangeLogs with reviewer.") + return + reviewer = self.guess_reviewer_from_bug(tool.bugs, bug_id) + + if not reviewer: + log("Failed to guess reviewer from bug %s and --reviewer= not provided. Not updating ChangeLogs with reviewer." % bug_id) + return + + for changelog_path in tool.scm().modified_changelogs(): + ChangeLog(changelog_path).set_reviewer(reviewer) + + def execute(self, options, args, tool): + bug_id = (args and args[0]) or parse_bug_id(tool.scm().create_patch()) + + EnsureBuildersAreGreenStep(tool, options).run() + + os.chdir(tool.scm().checkout_root) + self.update_changelogs_with_reviewer(options.reviewer, bug_id, tool) + + fake_patch = { + "id": None, + "bug_id": bug_id + } + + sequence = LandDiffSequence(fake_patch, options, tool) + sequence.run() + + +class AbstractPatchProcessingCommand(Command): + def __init__(self, help_text, args_description, options): + Command.__init__(self, help_text, args_description, options=options) + + def _fetch_list_of_patches_to_process(self, options, args, tool): + raise NotImplementedError, "subclasses must implement" + + def _prepare_to_process(self, options, args, tool): + raise NotImplementedError, "subclasses must implement" + + @staticmethod + def _collect_patches_by_bug(patches): + bugs_to_patches = {} + for patch in patches: + bug_id = patch["bug_id"] + bugs_to_patches[bug_id] = bugs_to_patches.get(bug_id, []) + [patch] + return bugs_to_patches + + def execute(self, options, args, tool): + self._prepare_to_process(options, args, tool) + patches = self._fetch_list_of_patches_to_process(options, args, tool) + + # It's nice to print out total statistics. + bugs_to_patches = self._collect_patches_by_bug(patches) + log("Processing %s from %s." % (pluralize("patch", len(patches)), pluralize("bug", len(bugs_to_patches)))) + + for patch in patches: + self._process_patch(patch, options, args, tool) + + +class CheckStyle(AbstractPatchProcessingCommand): + name = "check-style" + show_in_main_help = False + def __init__(self): + self._sequence = StepSequence([ + CleanWorkingDirectoryStep, + UpdateStep, + ApplyPatchStep, + CheckStyleStep, + ]) + AbstractPatchProcessingCommand.__init__(self, "Run check-webkit-style on the specified attachments", "ATTACHMENT_ID [ATTACHMENT_IDS]", self._sequence.options()) + + def _fetch_list_of_patches_to_process(self, options, args, tool): + return map(lambda patch_id: tool.bugs.fetch_attachment(patch_id), args) + + def _prepare_to_process(self, options, args, tool): + pass + + def _process_patch(self, patch, options, args, tool): + self._sequence.run_and_handle_errors(tool, options, patch) + + +class BuildAttachment(AbstractPatchProcessingCommand): + name = "build-attachment" + show_in_main_help = False + def __init__(self): + self._sequence = StepSequence([ + CleanWorkingDirectoryStep, + UpdateStep, + ApplyPatchStep, + BuildStep, + ]) + AbstractPatchProcessingCommand.__init__(self, "Apply and build patches from bugzilla", "ATTACHMENT_ID [ATTACHMENT_IDS]", self._sequence.options()) + + def _fetch_list_of_patches_to_process(self, options, args, tool): + return map(lambda patch_id: tool.bugs.fetch_attachment(patch_id), args) + + def _prepare_to_process(self, options, args, tool): + pass + + def _process_patch(self, patch, options, args, tool): + self._sequence.run_and_handle_errors(tool, options, patch) + + +class AbstractPatchLandingCommand(AbstractPatchProcessingCommand): + def __init__(self, help_text, args_description): + options = BuildSteps.cleaning_options() + options += BuildSteps.build_options() + options += BuildSteps.land_options() + AbstractPatchProcessingCommand.__init__(self, help_text, args_description, options) + + def _prepare_to_process(self, options, args, tool): + # Check the tree status first so we can fail early. + EnsureBuildersAreGreenStep(tool, options).run() + + def _process_patch(self, patch, options, args, tool): + sequence = LandingSequence(patch, options, tool) + sequence.run_and_handle_errors() + + +class LandAttachment(AbstractPatchLandingCommand): + name = "land-attachment" + show_in_main_help = True + def __init__(self): + AbstractPatchLandingCommand.__init__(self, "Land patches from bugzilla, optionally building and testing them first", "ATTACHMENT_ID [ATTACHMENT_IDS]") + + def _fetch_list_of_patches_to_process(self, options, args, tool): + return map(lambda patch_id: tool.bugs.fetch_attachment(patch_id), args) + + +class LandPatches(AbstractPatchLandingCommand): + name = "land-patches" + show_in_main_help = True + def __init__(self): + AbstractPatchLandingCommand.__init__(self, "Land all patches on the given bugs, optionally building and testing them first", "BUGID [BUGIDS]") + + def _fetch_list_of_patches_to_process(self, options, args, tool): + all_patches = [] + for bug_id in args: + patches = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) + log("%s found on bug %s." % (pluralize("reviewed patch", len(patches)), bug_id)) + all_patches += patches + return all_patches + + +# FIXME: Requires unit test. +class Rollout(Command): + name = "rollout" + show_in_main_help = True + def __init__(self): + options = BuildSteps.cleaning_options() + options += BuildSteps.build_options() + options += BuildSteps.land_options() + options.append(make_option("--complete-rollout", action="store_true", dest="complete_rollout", help="Commit the revert and re-open the original bug.")) + Command.__init__(self, "Revert the given revision in the working copy and optionally commit the revert and re-open the original bug", "REVISION [BUGID]", options=options) + + @staticmethod + def _create_changelogs_for_revert(tool, revision): + # First, discard the ChangeLog changes from the rollout. + changelog_paths = tool.scm().modified_changelogs() + 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. + PrepareChangelogStep(tool, None).run() + for changelog_path in changelog_paths: + ChangeLog(changelog_path).update_for_revert(revision) + + @staticmethod + def _parse_bug_id_from_revision_diff(tool, revision): + original_diff = tool.scm().diff_for_revision(revision) + return parse_bug_id(original_diff) + + @staticmethod + def _reopen_bug_after_rollout(tool, bug_id, comment_text): + if bug_id: + tool.bugs.reopen_bug(bug_id, comment_text) + else: + log(comment_text) + log("No bugs were updated or re-opened to reflect this rollout.") + + def execute(self, options, args, tool): + revision = args[0] + 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.") + + CleanWorkingDirectoryStep(tool, options).run() + UpdateStep(tool, options).run() + tool.scm().apply_reverse_diff(revision) + self._create_changelogs_for_revert(tool, revision) + + # 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 options.complete_rollout: + log("\nNOTE: Rollout support is experimental.\nPlease verify the rollout diff and use \"bugzilla-tool land-diff %s\" to commit the rollout." % bug_id) + else: + # FIXME: This function does not exist!! + # comment_text = WebKitLandingScripts.build_and_commit(tool.scm(), options) + raise ScriptError("OOPS! This option is not implemented (yet).") + self._reopen_bug_after_rollout(tool, bug_id, comment_text) diff --git a/WebKitTools/Scripts/modules/commands/download_unittest.py b/WebKitTools/Scripts/modules/commands/download_unittest.py new file mode 100644 index 0000000..a1ed41a --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/download_unittest.py @@ -0,0 +1,77 @@ +# 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 modules.commands.commandtest import CommandsTest +from modules.commands.download import * +from modules.mock import Mock + +class DownloadCommandsTest(CommandsTest): + def _default_options(self): + options = Mock() + options.force_clean = False + options.clean = True + options.check_builders = True + options.quiet = False + options.non_interactive = False + options.update = True + options.build = True + options.test = True + options.close_bug = True + return options + + def test_build(self): + self.assert_execute_outputs(Build(), [], options=self._default_options()) + + def test_apply_attachment(self): + options = self._default_options() + options.update = True + options.local_commit = True + self.assert_execute_outputs(ApplyAttachment(), [197], options=options) + + def test_apply_patches(self): + options = self._default_options() + options.update = True + options.local_commit = True + self.assert_execute_outputs(ApplyPatches(), [42], options=options) + + def test_land_diff(self): + self.assert_execute_outputs(LandDiff(), [42], options=self._default_options()) + + def test_check_style(self): + self.assert_execute_outputs(CheckStyle(), [197], options=self._default_options()) + + def test_build_attachment(self): + self.assert_execute_outputs(BuildAttachment(), [197], options=self._default_options()) + + def test_land_attachment(self): + self.assert_execute_outputs(LandAttachment(), [197], options=self._default_options()) + + def test_land_patches(self): + self.assert_execute_outputs(LandPatches(), [42], options=self._default_options()) diff --git a/WebKitTools/Scripts/modules/commands/early_warning_system.py b/WebKitTools/Scripts/modules/commands/early_warning_system.py new file mode 100644 index 0000000..e8ef408 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/early_warning_system.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# 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 modules.commands.queues import AbstractReviewQueue +from modules.executive import ScriptError +from modules.webkitport import WebKitPort + +class AbstractEarlyWarningSystem(AbstractReviewQueue): + def __init__(self): + AbstractReviewQueue.__init__(self) + self.port = WebKitPort.port(self.port_name) + + def should_proceed_with_work_item(self, patch): + try: + self.run_bugzilla_tool(["build", self.port.flag(), "--force-clean", "--quiet"]) + except ScriptError, e: + return (False, "Unable to perform a build.", None) + return (True, "Building patch %s on bug %s." % (patch["id"], patch["bug_id"]), patch) + + def process_work_item(self, patch): + self.run_bugzilla_tool([ + "build-attachment", + self.port.flag(), + "--force-clean", + "--quiet", + "--non-interactive", + "--parent-command=%s" % self.name, + "--no-update", + patch["id"]]) + self._patches.did_pass(patch) + + +class QtEWS(AbstractEarlyWarningSystem): + name = "qt-ews" + port_name = "qt" + + +class ChromiumEWS(AbstractEarlyWarningSystem): + name = "chromium-ews" + port_name = "chromium" diff --git a/WebKitTools/Scripts/modules/commands/queries.py b/WebKitTools/Scripts/modules/commands/queries.py new file mode 100644 index 0000000..98310e3 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/queries.py @@ -0,0 +1,135 @@ +#!/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 modules.buildbot import BuildBot +from modules.committers import CommitterList +from modules.logging import log +from modules.multicommandtool import Command + + +class BugsToCommit(Command): + name = "bugs-to-commit" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "List bugs in the commit-queue") + + def execute(self, options, args, tool): + bug_ids = tool.bugs.fetch_bug_ids_from_commit_queue() + for bug_id in bug_ids: + print "%s" % bug_id + + +class PatchesToCommit(Command): + name = "patches-to-commit" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "List patches in the commit-queue") + + def execute(self, options, args, tool): + patches = tool.bugs.fetch_patches_from_commit_queue() + log("Patches in commit queue:") + for patch in patches: + print "%s" % patch["url"] + + +class PatchesToCommitQueue(Command): + name = "patches-to-commit-queue" + show_in_main_help = False + def __init__(self): + options = [ + make_option("--bugs", action="store_true", dest="bugs", help="Output bug links instead of patch links"), + ] + Command.__init__(self, "Patches which should be added to the commit queue", options=options) + + @staticmethod + def _needs_commit_queue(patch): + commit_queue_flag = patch.get("commit-queue") + if (commit_queue_flag and commit_queue_flag == '+'): # If it's already cq+, ignore the patch. + log("%s already has cq=%s" % (patch["id"], commit_queue_flag)) + 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.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(Command): + name = "patches-to-review" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "List patches that are pending review") + + def execute(self, options, args, tool): + patch_ids = tool.bugs.fetch_attachment_ids_from_review_queue() + log("Patches pending review:") + for patch_id in patch_ids: + print patch_id + + +class ReviewedPatches(Command): + name = "reviewed-patches" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "List r+'d patches on a bug", "BUGID") + + def execute(self, options, args, tool): + bug_id = args[0] + patches_to_land = tool.bugs.fetch_reviewed_patches_from_bug(bug_id) + for patch in patches_to_land: + print "%s" % patch["url"] + + +class TreeStatus(Command): + name = "tree-status" + show_in_main_help = True + def __init__(self): + Command.__init__(self, "Print the status of the %s buildbots" % BuildBot.default_host) + + 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/modules/commands/queries_unittest.py b/WebKitTools/Scripts/modules/commands/queries_unittest.py new file mode 100644 index 0000000..0d1c82a --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/queries_unittest.py @@ -0,0 +1,68 @@ +# 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 modules.bugzilla import Bugzilla +from modules.commands.commandtest import CommandsTest +from modules.commands.queries import * +from modules.mock import Mock +from modules.mock_bugzillatool import MockBugzillaTool + +class QueryCommandsTest(CommandsTest): + def test_bugs_to_commit(self): + self.assert_execute_outputs(BugsToCommit(), None, "42\n75\n") + + def test_patches_to_commit(self): + expected_stdout = "http://example.com/197\nhttp://example.com/128\n" + expected_stderr = "Patches in commit queue:\n" + self.assert_execute_outputs(PatchesToCommit(), None, expected_stdout, expected_stderr) + + def test_patches_to_commit_queue(self): + expected_stdout = "http://example.com/197&action=edit\nhttp://example.com/128&action=edit\n" + expected_stderr = "" + options = Mock() + options.bugs = False + self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options) + + expected_stdout = "http://example.com/42\n" + options.bugs = True + self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options) + + def test_patches_to_review(self): + expected_stdout = "197\n128\n" + expected_stderr = "Patches pending review:\n" + self.assert_execute_outputs(PatchesToReview(), None, expected_stdout, expected_stderr) + + def test_reviewed_patches(self): + expected_stdout = "http://example.com/197\nhttp://example.com/128\n" + self.assert_execute_outputs(ReviewedPatches(), [42], expected_stdout) + + def test_tree_status(self): + expected_stdout = "ok : Builder1\nok : Builder2\n" + self.assert_execute_outputs(TreeStatus(), None, expected_stdout) diff --git a/WebKitTools/Scripts/modules/commands/queues.py b/WebKitTools/Scripts/modules/commands/queues.py new file mode 100644 index 0000000..53b9e48 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/queues.py @@ -0,0 +1,216 @@ +#!/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. + +import re + +from datetime import datetime +from optparse import make_option + +from modules.executive import ScriptError +from modules.grammar import pluralize +from modules.landingsequence import LandingSequence, LandingSequenceErrorHandler +from modules.logging import error, log +from modules.multicommandtool import Command +from modules.patchcollection import PersistentPatchCollection, PersistentPatchCollectionDelegate +from modules.statusbot import StatusBot +from modules.workqueue import WorkQueue, WorkQueueDelegate + +class AbstractQueue(Command, WorkQueueDelegate): + show_in_main_help = False + watchers = "webkit-bot-watchers@googlegroups.com" + 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("--status-host", action="store", type="string", dest="status_host", default=StatusBot.default_host, help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted."), + ] + Command.__init__(self, "Run the %s" % self.name, options=options_list) + + def _cc_watchers(self, bug_id): + try: + self.tool.bugs.add_cc_to_bug(bug_id, self.watchers) + except Exception, e: + log("Failed to CC watchers: %s." % e) + + def queue_log_path(self): + return "%s.log" % self.name + + def work_logs_directory(self): + return "%s-logs" % self.name + + def status_host(self): + return self.options.status_host + + def begin_work_queue(self): + log("CAUTION: %s will discard all local changes in %s" % (self.name, self.tool.scm().checkout_root)) + if self.options.confirm: + response = raw_input("Are you sure? Type \"yes\" to continue: ") + if (response != "yes"): + error("User declined.") + log("Running WebKit %s. %s" % (self.name, datetime.now().strftime(WorkQueue.log_date_format))) + + def should_continue_work_queue(self): + return True + + def next_work_item(self): + raise NotImplementedError, "subclasses must implement" + + def should_proceed_with_work_item(self, work_item): + raise NotImplementedError, "subclasses must implement" + + def process_work_item(self, work_item): + raise NotImplementedError, "subclasses must implement" + + def handle_unexpected_error(self, work_item, message): + raise NotImplementedError, "subclasses must implement" + + def run_bugzilla_tool(self, args): + bugzilla_tool_args = [self.tool.path()] + map(str, args) + self.tool.executive.run_and_throw_if_fail(bugzilla_tool_args) + + def log_progress(self, patch_ids): + log("%s in %s [%s]" % (pluralize("patch", len(patch_ids)), self.name, ", ".join(map(str, patch_ids)))) + + def execute(self, options, args, tool): + self.options = options + self.tool = tool + work_queue = WorkQueue(self.name, self) + return work_queue.run() + + +class CommitQueue(AbstractQueue, LandingSequenceErrorHandler): + name = "commit-queue" + def __init__(self): + AbstractQueue.__init__(self) + + # AbstractQueue methods + + def begin_work_queue(self): + AbstractQueue.begin_work_queue(self) + + def next_work_item(self): + patches = self.tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) + if not patches: + return None + # Only bother logging if we have patches in the queue. + self.log_progress([patch['id'] for patch in patches]) + return patches[0] + + def should_proceed_with_work_item(self, patch): + red_builders_names = self.tool.buildbot.red_core_builders_names() + if red_builders_names: + red_builders_names = map(lambda name: "\"%s\"" % name, red_builders_names) # Add quotes around the names. + return (False, "Builders [%s] are red. See http://build.webkit.org." % ", ".join(red_builders_names), None) + return (True, "Landing patch %s from bug %s." % (patch["id"], patch["bug_id"]), patch) + + def process_work_item(self, patch): + self._cc_watchers(patch["bug_id"]) + self.run_bugzilla_tool(["land-attachment", "--force-clean", "--non-interactive", "--parent-command=commit-queue", "--quiet", patch["id"]]) + + def handle_unexpected_error(self, patch, message): + self.tool.bugs.reject_patch_from_commit_queue(patch["id"], message) + + # LandingSequenceErrorHandler methods + + @classmethod + def handle_script_error(cls, tool, patch, script_error): + tool.bugs.reject_patch_from_commit_queue(patch["id"], script_error.message_with_output()) + + +class AbstractReviewQueue(AbstractQueue, PersistentPatchCollectionDelegate, LandingSequenceErrorHandler): + def __init__(self, options=None): + AbstractQueue.__init__(self, options) + + # PersistentPatchCollectionDelegate methods + + def collection_name(self): + return self.name + + def fetch_potential_patch_ids(self): + return self.tool.bugs.fetch_attachment_ids_from_review_queue() + + def status_server(self): + return self.tool.status() + + # AbstractQueue methods + + def begin_work_queue(self): + AbstractQueue.begin_work_queue(self) + self.tool.status().set_host(self.options.status_host) + self._patches = PersistentPatchCollection(self) + + def next_work_item(self): + patch_id = self._patches.next() + if patch_id: + return self.tool.bugs.fetch_attachment(patch_id) + + def should_proceed_with_work_item(self, patch): + raise NotImplementedError, "subclasses must implement" + + def process_work_item(self, patch): + raise NotImplementedError, "subclasses must implement" + + def handle_unexpected_error(self, patch, message): + log(message) + + # LandingSequenceErrorHandler methods + + @classmethod + def handle_script_error(cls, tool, patch, script_error): + log(script_error.message_with_output()) + + +class StyleQueue(AbstractReviewQueue): + name = "style-queue" + def __init__(self): + AbstractReviewQueue.__init__(self) + + def should_proceed_with_work_item(self, patch): + return (True, "Checking style for patch %s on bug %s." % (patch["id"], patch["bug_id"]), patch) + + def process_work_item(self, patch): + try: + self.run_bugzilla_tool(["check-style", "--force-clean", "--non-interactive", "--parent-command=style-queue", patch["id"]]) + message = "%s ran check-webkit-style on attachment %s without any errors." % (self.name, patch["id"]) + self.tool.bugs.post_comment_to_bug(patch["bug_id"], message, cc=self.watchers) + self._patches.did_pass(patch) + except ScriptError, e: + self._patches.did_fail(patch) + raise e + + @classmethod + def handle_script_error(cls, tool, patch, script_error): + command = script_error.script_args + if type(command) is list: + command = command[0] + # FIXME: We shouldn't need to use a regexp here. ScriptError should + # have a better API. + if re.search("check-webkit-style", command): + message = "Attachment %s did not pass %s:\n\n%s" % (patch["id"], cls.name, script_error.message_with_output(output_limit=5*1024)) + tool.bugs.post_comment_to_bug(patch["bug_id"], message, cc=cls.watchers) diff --git a/WebKitTools/Scripts/modules/commands/queues_unittest.py b/WebKitTools/Scripts/modules/commands/queues_unittest.py new file mode 100644 index 0000000..75abbe5 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/queues_unittest.py @@ -0,0 +1,66 @@ +# 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 modules.commands.commandtest import CommandsTest +from modules.commands.queues import * +from modules.mock_bugzillatool import MockBugzillaTool +from modules.outputcapture import OutputCapture + + +class TestQueue(AbstractQueue): + name = "test-queue" + + +class AbstractQueueTest(CommandsTest): + def _assert_output(self, function, args, expected_stdout="", expected_stderr=""): + capture = OutputCapture() + capture.capture_output() + function(*args) + (stdout_string, stderr_string) = capture.restore_output() + self.assertEqual(stdout_string, expected_stdout) + self.assertEqual(stderr_string, expected_stderr) + + def _assert_log_progress_output(self, patch_ids, progress_output): + self._assert_output(TestQueue().log_progress, [patch_ids], expected_stderr=progress_output) + + def test_log_progress(self): + self._assert_log_progress_output([1,2,3], "3 patches in test-queue [1, 2, 3]\n") + 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 _assert_run_bugzilla_tool_output(self, run_args, tool_output): + queue = TestQueue() + queue.bind_to_tool(MockBugzillaTool()) + # MockBugzillaTool.path() is "echo" + self._assert_output(queue.run_bugzilla_tool, [run_args], expected_stdout=tool_output) + + def test_run_bugzilla_tool(self): + self._assert_run_bugzilla_tool_output([1], "") + self._assert_run_bugzilla_tool_output(["one", 2], "") diff --git a/WebKitTools/Scripts/modules/commands/upload.py b/WebKitTools/Scripts/modules/commands/upload.py new file mode 100644 index 0000000..1f892a1 --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/upload.py @@ -0,0 +1,246 @@ +#!/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. + +import os +import StringIO +import sys + +from optparse import make_option + +from modules.bugzilla import parse_bug_id +from modules.grammar import pluralize +from modules.logging import error, log +from modules.multicommandtool import Command + +# FIXME: Requires unit test. +class CommitMessageForCurrentDiff(Command): + name = "commit-message" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "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() + + +class ObsoleteAttachments(Command): + name = "obsolete-attachments" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "Mark all attachments on a bug as obsolete", "BUGID") + + def execute(self, options, args, tool): + bug_id = args[0] + attachments = tool.bugs.fetch_attachments_from_bug(bug_id) + for attachment in attachments: + if not attachment["is_obsolete"]: + tool.bugs.obsolete_attachment(attachment["id"]) + + +class PostDiff(Command): + name = "post-diff" + show_in_main_help = True + def __init__(self): + options = [ + make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")"), + ] + options += self.posting_options() + Command.__init__(self, "Attach the current working directory diff to a bug as a patch file", "[BUGID]", options=options) + + @staticmethod + def posting_options(): + return [ + make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one."), + make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), + ] + + @staticmethod + def obsolete_patches_on_bug(bug_id, bugs): + patches = bugs.fetch_patches_from_bug(bug_id) + if len(patches): + log("Obsoleting %s on bug %s" % (pluralize("old patch", len(patches)), bug_id)) + for patch in patches: + bugs.obsolete_attachment(patch["id"]) + + def execute(self, options, args, tool): + # Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs). + bug_id = (args and args[0]) or parse_bug_id(tool.scm().create_patch()) + if not bug_id: + error("No bug id passed and no bug url found in diff, can't post.") + + if options.obsolete_patches: + self.obsolete_patches_on_bug(bug_id, tool.bugs) + + diff = tool.scm().create_patch() + diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object + + description = options.description or "Patch" + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) + + +class PostCommits(Command): + name = "post-commits" + show_in_main_help = True + def __init__(self): + options = [ + make_option("-b", "--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."), + make_option("--add-log-as-comment", action="store_true", dest="add_log_as_comment", default=False, help="Add commit log message as a comment when uploading the patch."), + make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: description from commit message)"), + ] + options += PostDiff.posting_options() + Command.__init__(self, "Attach a range of local commits to bugs as patch files", "COMMITISH", options=options, requires_local_commits=True) + + def _comment_text_for_commit(self, options, commit_message, tool, commit_id): + comment_text = None + if (options.add_log_as_comment): + comment_text = commit_message.body(lstrip=True) + comment_text += "---\n" + comment_text += tool.scm().files_changed_summary_for_commit(commit_id) + return comment_text + + def _diff_file_for_commit(self, tool, commit_id): + diff = tool.scm().create_patch_from_local_commit(commit_id) + return StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object + + def execute(self, options, args, tool): + commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) + if len(commit_ids) > 10: # We could lower this limit, 10 is too many for one bug as-is. + error("bugzilla-tool does not support attaching %s at once. Are you sure you passed the right commit range?" % (pluralize("patch", len(commit_ids)))) + + have_obsoleted_patches = set() + for commit_id in commit_ids: + commit_message = tool.scm().commit_message_for_local_commit(commit_id) + + # Prefer --bug-id=, then a bug url in the commit message, then a bug url in the entire commit diff (i.e. ChangeLogs). + bug_id = options.bug_id or parse_bug_id(commit_message.message()) or parse_bug_id(tool.scm().create_patch_from_local_commit(commit_id)) + if not bug_id: + log("Skipping %s: No bug id found in commit or specified with --bug-id." % commit_id) + continue + + if options.obsolete_patches and bug_id not in have_obsoleted_patches: + PostDiff.obsolete_patches_on_bug(bug_id, tool.bugs) + have_obsoleted_patches.add(bug_id) + + diff_file = self._diff_file_for_commit(tool, commit_id) + description = options.description or commit_message.description(lstrip=True, strip_url=True) + comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id) + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) + + +class MarkFixed(Command): + name = "mark-fixed" + show_in_main_help = False + def __init__(self): + Command.__init__(self, "Mark the specified bug as fixed", "BUG_ID REASON") + + def execute(self, options, args, tool): + tool.bugs.close_bug_as_fixed(args[0], args[1]) + + +# FIXME: Requires unit test. Blocking issue: too complex for now. +class CreateBug(Command): + name = "create-bug" + show_in_main_help = True + def __init__(self): + options = [ + make_option("--cc", action="store", type="string", dest="cc", help="Comma-separated list of email addresses to carbon-copy."), + make_option("--component", action="store", type="string", dest="component", help="Component for the new bug."), + make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."), + make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), + ] + Command.__init__(self, "Create a bug from local changes or local commits", "[COMMITISH]", options=options) + + def create_bug_from_commit(self, options, args, tool): + commit_ids = tool.scm().commit_ids_from_commitish_arguments(args) + if len(commit_ids) > 3: + error("Are you sure you want to create one bug with %s patches?" % len(commit_ids)) + + commit_id = commit_ids[0] + + bug_title = "" + comment_text = "" + if options.prompt: + (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() + else: + commit_message = tool.scm().commit_message_for_local_commit(commit_id) + bug_title = commit_message.description(lstrip=True, strip_url=True) + comment_text = commit_message.body(lstrip=True) + comment_text += "---\n" + comment_text += tool.scm().files_changed_summary_for_commit(commit_id) + + diff = tool.scm().create_patch_from_local_commit(commit_id) + diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) + + if bug_id and len(commit_ids) > 1: + options.bug_id = bug_id + options.obsolete_patches = False + # FIXME: We should pass through --no-comment switch as well. + PostCommits.execute(self, options, commit_ids[1:], tool) + + def create_bug_from_patch(self, options, args, tool): + bug_title = "" + comment_text = "" + if options.prompt: + (bug_title, comment_text) = self.prompt_for_bug_title_and_comment() + else: + commit_message = tool.scm().commit_message_for_this_commit() + bug_title = commit_message.description(lstrip=True, strip_url=True) + comment_text = commit_message.body(lstrip=True) + + diff = tool.scm().create_patch() + diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) + + def prompt_for_bug_title_and_comment(self): + bug_title = raw_input("Bug title: ") + print "Bug comment (hit ^D on blank line to end):" + lines = sys.stdin.readlines() + try: + sys.stdin.seek(0, os.SEEK_END) + except IOError: + # Cygwin raises an Illegal Seek (errno 29) exception when the above + # seek() call is made. Ignoring it seems to cause no harm. + # FIXME: Figure out a way to get avoid the exception in the first + # place. + pass + comment_text = "".join(lines) + return (bug_title, comment_text) + + def execute(self, options, args, tool): + if len(args): + if (not tool.scm().supports_local_commits()): + error("Extra arguments not supported; patch is taken from working directory.") + self.create_bug_from_commit(options, args, tool) + else: + self.create_bug_from_patch(options, args, tool) diff --git a/WebKitTools/Scripts/modules/commands/upload_unittest.py b/WebKitTools/Scripts/modules/commands/upload_unittest.py new file mode 100644 index 0000000..4d3f85c --- /dev/null +++ b/WebKitTools/Scripts/modules/commands/upload_unittest.py @@ -0,0 +1,42 @@ +# 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 modules.commands.commandtest import CommandsTest +from modules.commands.upload import * + +class UploadCommandsTest(CommandsTest): + def test_mark_fixed(self): + self.assert_execute_outputs(MarkFixed(), [43, "Test comment"]) + + def test_obsolete_attachments(self): + self.assert_execute_outputs(ObsoleteAttachments(), [42]) + + def test_post_diff(self): + self.assert_execute_outputs(PostDiff(), [42]) diff --git a/WebKitTools/Scripts/modules/committers.py b/WebKitTools/Scripts/modules/committers.py index fc263eb..d32a536 100644 --- a/WebKitTools/Scripts/modules/committers.py +++ b/WebKitTools/Scripts/modules/committers.py @@ -29,128 +29,176 @@ # WebKit's Python module for committer and reviewer validation class Committer: - def __init__(self, name, email): + def __init__(self, name, email_or_emails): self.full_name = name - self.bugzilla_email = email + if isinstance(email_or_emails, str): + self.emails = [email_or_emails] + else: + self.emails = email_or_emails self.can_review = False def __str__(self): - return '"%s" <%s>' % (self.full_name, self.bugzilla_email) + return '"%s" <%s>' % (self.full_name, self.emails[0]) class Reviewer(Committer): - def __init__(self, name, email): - Committer.__init__(self, name, email) + def __init__(self, name, email_or_emails): + Committer.__init__(self, name, email_or_emails) self.can_review = True -# This is intended as a cannonical, machine-readable list of all non-reviewer committers for WebKit. +# This is intended as a canonical, machine-readable list of all non-reviewer committers for WebKit. # If your name is missing here and you are a committer, please add it. No review needed. # All reviewers are committers, so this list is only of committers who are not reviewers. committers_unable_to_review = [ Committer("Aaron Boodman", "aa@chromium.org"), Committer("Adam Langley", "agl@chromium.org"), Committer("Albert J. Wong", "ajwong@chromium.org"), + Committer("Alexander Kellett", ["lypanov@mac.com", "a-lists001@lypanov.net", "lypanov@kde.org"]), + Committer("Andre Boule", "aboule@apple.com"), + Committer("Andrew Wellington", ["andrew@webkit.org", "proton@wiretapped.net"]), + Committer("Anthony Ricaud", "rik@webkit.org"), Committer("Anton Muhin", "antonm@chromium.org"), Committer("Antonio Gomes", "tonikitoo@webkit.org"), - Committer("Anthony Ricaud", "rik@webkit.org"), Committer("Ben Murdoch", "benm@google.com"), - Committer("Chris Fleizach", "cfleizach@apple.com"), + Committer("Benjamin C Meyer", ["ben@meyerhome.net", "ben@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("Chris Fleizach", "cfleizach@apple.com"), + Committer("Chris Marrin", "cmarrin@apple.com"), + Committer("Chris Petersen", "cpetersen@apple.com"), + 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("Drew Wilson", "atwilson@chromium.org"), - Committer("Dirk Schulze", "krit@webkit.org"), - Committer("Dmitry Titov", "dimich@chromium.org"), Committer("Dumitru Daniliuc", "dumi@chromium.org"), Committer("Eli Fidler", "eli@staikos.net"), + Committer("Enrica Casucci", "enrica@apple.com"), + Committer("Erik Arvidsson", "arv@chromium.org"), Committer("Eric Roman", "eroman@chromium.org"), + Committer("Feng Qian", "feng@chromium.org"), Committer("Fumitoshi Ukai", "ukai@chromium.org"), + 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("Jeremy Moskovich", "playmobil@google.com"), + Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]), + Committer("Jens Alfke", ["snej@chromium.org", "jens@apple.com"]), + Committer("Jeremy Moskovich", ["playmobil@google.com", "jeremy@chromium.org"]), Committer("Jeremy Orlow", "jorlow@chromium.org"), + 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"), - Committer("Kenneth Rohde Christiansen", "kenneth@webkit.org"), + Committer("Julie Parent", ["jparent@google.com", "jparent@chromium.org"]), + Committer("Julien Chaffraix", ["jchaffraix@webkit.org", "julien.chaffraix@gmail.com"]), + Committer("Jungshik Shin", "jshin@chromium.org"), + Committer("Keishi Hattori", "keishi@webkit.org"), + Committer("Kelly Norton", "knorton@google.com"), Committer("Kent Tamura", "tkent@chromium.org"), + Committer("Krzysztof Kowalczyk", "kkowalczyk@gmail.com"), Committer("Laszlo Gombos", "laszlo.1.gombos@nokia.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("Michelangelo De Simone", "michelangelo@webkit.org"), Committer("Mike Belshe", "mike@belshe.com"), + Committer("Mike Fenton", ["mike.fenton@torchmobile.com", "mifenton@rim.com"]), + Committer("Mike Thole", ["mthole@mikethole.com", "mthole@apple.com"]), Committer("Nate Chapin", "japhet@chromium.org"), Committer("Ojan Vafai", "ojan@chromium.org"), Committer("Pam Greene", "pam@chromium.org"), - Committer("Peter Kasting", "pkasting@google.com"), - Committer("Pierre d'Herbemont", "pdherbemont@free.fr"), + Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"]), + Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"]), + Committer("Pierre-Olivier Latour", "pol@apple.com"), Committer("Roland Steiner", "rolandsteiner@chromium.org"), Committer("Ryosuke Niwa", "rniwa@webkit.org"), Committer("Scott Violet", "sky@chromium.org"), Committer("Shinichiro Hamaji", "hamaji@chromium.org"), + Committer("Stephen White", "senorblanco@chromium.org"), Committer("Steve Block", "steveblock@google.com"), Committer("Tony Chang", "tony@chromium.org"), + Committer("Trey Matteson", "trey@usa.net"), + Committer("Tristan O'Tierney", ["tristan@otierney.net", "tristan@apple.com"]), + Committer("William Siegrist", "wsiegrist@apple.com"), Committer("Yael Aharon", "yael.aharon@nokia.com"), - Committer("Yong Li", "yong.li@torchmobile.com"), + Committer("Yaar Schnitman", ["yaar@chromium.org", "yaar@google.com"]), + Committer("Yong Li", ["yong.li@torchmobile.com", "yong.li.webkit@gmail.com"]), + Committer("Yongjun Zhang", "yongjun.zhang@nokia.com"), Committer("Yury Semikhatsky", "yurys@chromium.org"), Committer("Zoltan Horvath", "zoltan@webkit.org"), ] -# This is intended as a cannonical, machine-readable list of all reviewers for WebKit. +# This is intended as a canonical, machine-readable list of all reviewers for WebKit. # If your name is missing here and you are a reviewer, please add it. No review needed. reviewers_list = [ - Reviewer("Adam Barth", "abarth@webkit.org"), Reviewer("Ada Chan", "adachan@apple.com"), + Reviewer("Adam Barth", "abarth@webkit.org"), Reviewer("Adam Roben", "aroben@apple.com"), - Reviewer("Adam Treat", "treat@kde.org"), + Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org"]), Reviewer("Adele Peterson", "adele@apple.com"), - Reviewer("Alexey Proskuryakov", "ap@webkit.org"), + Reviewer("Alexey Proskuryakov", ["ap@webkit.org", "ap@apple.com"]), Reviewer("Alice Liu", "alice.liu@apple.com"), - Reviewer("Alp Toker", "alp@nuanti.com"), - Reviewer("Anders Carlsson", "andersca@apple.com"), - Reviewer("Antti Koivisto", "koivisto@iki.fi"), - Reviewer("Ariya Hidayat", "ariya.hidayat@trolltech.com"), + Reviewer("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@trolltech.com", "ariya.hidayat@gmail.com", "ariya@webkit.org"]), + Reviewer("Beth Dakin", "bdakin@apple.com"), Reviewer("Brady Eidson", "beidson@apple.com"), - Reviewer("Cameron Zwarich", "zwarich@apple.com"), - Reviewer("Dan Bernstein", "mitz@webkit.org"), + 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"), + 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"), + 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("Dmitri Titov", "dimich@chromium.org"), 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("George Staikos", "staikos@kde.org"), - Reviewer("Gustavo Noronha", "gns@gnome.org"), - Reviewer("Holger Freyther", "zecke@selfish.org"), - Reviewer("Jan Alonzo", "jmalonzo@gmail.com"), + 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("John Sullivan", "sullivan@apple.com"), Reviewer("Jon Honeycutt", "jhoneycutt@apple.com"), Reviewer("Justin Garcia", "justin.garcia@apple.com"), + 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"), - Reviewer("Lars Knoll", "lars@trolltech.com"), + Reviewer("Kevin Ollivier", ["kevino@theolliviers.com", "kevino@webkit.org"]), + Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org"]), Reviewer("Maciej Stachowiak", "mjs@apple.com"), Reviewer("Mark Rowe", "mrowe@apple.com"), - Reviewer("Nikolas Zimmermann", "zimmermann@kde.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("Rob Buis", "rwlbuis@gmail.com"), - Reviewer("Sam Weinig", "sam@webkit.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("Simon Fraser", "simon.fraser@apple.com"), - Reviewer("Simon Hausmann", "hausmann@webkit.org"), + Reviewer("Simon Hausmann", ["hausmann@webkit.org", "hausmann@kde.org"]), Reviewer("Stephanie Lewis", "slewis@apple.com"), Reviewer("Steve Falkenburg", "sfalken@apple.com"), - Reviewer("Timothy Hatcher", "timothy@hatcher.name"), + 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("Xan Lopez", "xan.lopez@gmail.com"), + 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"), ] @@ -164,17 +212,21 @@ class CommitterList: def committers(self): return self._committers + def reviewers(self): + return self._reviewers + def _email_to_committer_map(self): if not len(self._committers_by_email): for committer in self._committers: - self._committers_by_email[committer.bugzilla_email] = committer + for email in committer.emails: + self._committers_by_email[email] = committer return self._committers_by_email - def committer_by_bugzilla_email(self, bugzilla_email): - return self._email_to_committer_map().get(bugzilla_email) + def committer_by_email(self, email): + return self._email_to_committer_map().get(email) - def reviewer_by_bugzilla_email(self, bugzilla_email): - committer = self.committer_by_bugzilla_email(bugzilla_email) + def reviewer_by_email(self, email): + committer = self.committer_by_email(email) if committer and not committer.can_review: return None return committer diff --git a/WebKitTools/Scripts/modules/committers_unittest.py b/WebKitTools/Scripts/modules/committers_unittest.py index 045e20e..cf9f486 100644 --- a/WebKitTools/Scripts/modules/committers_unittest.py +++ b/WebKitTools/Scripts/modules/committers_unittest.py @@ -33,20 +33,30 @@ class CommittersTest(unittest.TestCase): def test_committer_lookup(self): committer = Committer('Test One', 'one@test.com') - reviewer = Reviewer('Test Two', 'two@test.com') + reviewer = Reviewer('Test Two', ['two@test.com', 'two@rad.com', 'so_two@gmail.com']) committer_list = CommitterList(committers=[committer], reviewers=[reviewer]) # Test valid committer and reviewer lookup - self.assertEqual(committer_list.committer_by_bugzilla_email('one@test.com'), committer) - self.assertEqual(committer_list.reviewer_by_bugzilla_email('two@test.com'), reviewer) - self.assertEqual(committer_list.committer_by_bugzilla_email('two@test.com'), reviewer) + self.assertEqual(committer_list.committer_by_email('one@test.com'), committer) + self.assertEqual(committer_list.reviewer_by_email('two@test.com'), reviewer) + self.assertEqual(committer_list.committer_by_email('two@test.com'), reviewer) + self.assertEqual(committer_list.committer_by_email('two@rad.com'), reviewer) + self.assertEqual(committer_list.reviewer_by_email('so_two@gmail.com'), reviewer) # Test that a known committer is not returned during reviewer lookup - self.assertEqual(committer_list.reviewer_by_bugzilla_email('one@test.com'), None) + self.assertEqual(committer_list.reviewer_by_email('one@test.com'), None) # Test that unknown email address fail both committer and reviewer lookup - self.assertEqual(committer_list.committer_by_bugzilla_email('bar@bar.com'), None) - self.assertEqual(committer_list.reviewer_by_bugzilla_email('bar@bar.com'), None) + self.assertEqual(committer_list.committer_by_email('bar@bar.com'), None) + self.assertEqual(committer_list.reviewer_by_email('bar@bar.com'), None) + + # Test that emails returns a list. + self.assertEqual(committer.emails, ['one@test.com']) + + # Test that committers returns committers and reviewers and reviewers() just reviewers. + self.assertEqual(committer_list.committers(), [committer, reviewer]) + self.assertEqual(committer_list.reviewers(), [reviewer]) + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/modules/cpp_style.py b/WebKitTools/Scripts/modules/cpp_style.py index 485b07c..d8ca8d1 100644 --- a/WebKitTools/Scripts/modules/cpp_style.py +++ b/WebKitTools/Scripts/modules/cpp_style.py @@ -130,6 +130,7 @@ _ERROR_CATEGORIES = '''\ readability/function readability/multiline_comment readability/multiline_string + readability/naming readability/null readability/streams readability/todo @@ -243,14 +244,14 @@ _PRIMARY_HEADER = 1 _OTHER_HEADER = 2 +# The regexp compilation caching is inlined in all regexp functions for +# performance reasons; factoring it out into a separate function turns out +# to be noticeably expensive. _regexp_compile_cache = {} def match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both match and search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) @@ -263,6 +264,20 @@ def search(pattern, s): return _regexp_compile_cache[pattern].search(s) +def sub(pattern, replacement, s): + """Substitutes occurrences of a pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].sub(replacement, s) + + +def subn(pattern, replacement, s): + """Substitutes occurrences of a pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].subn(replacement, s) + + class _IncludeState(dict): """Tracks line numbers for includes, and the order in which includes appear. @@ -868,7 +883,7 @@ def get_header_guard_cpp_variable(filename): """ fileinfo = FileInfo(filename) - return re.sub(r'[-./\s]', '_', fileinfo.repository_name()).upper() + '_' + return sub(r'[-./\s]', '_', fileinfo.repository_name()).upper() + '_' def check_for_header_guard(filename, lines, error): @@ -1119,6 +1134,16 @@ class _ClassState(object): self.classinfo_stack[0].name) +class _FileState(object): + def __init__(self): + self._did_inside_namespace_indent_warning = False + + def set_did_inside_namespace_indent_warning(self): + self._did_inside_namespace_indent_warning = True + + def did_inside_namespace_indent_warning(self): + return self._did_inside_namespace_indent_warning + def check_for_non_standard_constructs(filename, clean_lines, line_number, class_state, error): """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. @@ -1532,10 +1557,10 @@ def check_spacing(filename, clean_lines, line_number, error): line = clean_lines.elided[line_number] # get rid of comments and strings # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - # Don't try to do spacing checks for #include statements at minimum it - # messes up checks for spacing around / - if match(r'\s*#\s*include', line): + line = sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + # Don't try to do spacing checks for #include or #import statements at + # minimum because it messes up checks for spacing around / + if match(r'\s*#\s*(?:include|import)', line): return if search(r'[\w.]=[\w.]', line): error(filename, line_number, 'whitespace/operators', 4, @@ -1675,7 +1700,7 @@ def get_previous_non_blank_line(clean_lines, line_number): return ('', -1) -def check_namespace_indentation(filename, clean_lines, line_number, file_extension, error): +def check_namespace_indentation(filename, clean_lines, line_number, file_extension, file_state, error): """Looks for indentation errors inside of namespaces. Args: @@ -1683,6 +1708,8 @@ def check_namespace_indentation(filename, clean_lines, line_number, file_extensi clean_lines: A CleansedLines instance containing the file. line_number: The number of the line to check. file_extension: The extension (dot not included) of the file. + file_state: A _FileState instance which maintains information about + the state of things in the file. error: The function to call with any errors found. """ @@ -1694,8 +1721,10 @@ def check_namespace_indentation(filename, clean_lines, line_number, file_extensi current_indentation_level = len(namespace_match.group('namespace_indentation')) if current_indentation_level > 0: - error(filename, line_number, 'whitespace/indent', 4, - 'namespace should never be indented.') + # Don't warn about an indented namespace if we already warned about indented code. + if not file_state.did_inside_namespace_indent_warning(): + error(filename, line_number, 'whitespace/indent', 4, + 'namespace should never be indented.') return looking_for_semicolon = False; line_offset = 0 @@ -1706,7 +1735,8 @@ def check_namespace_indentation(filename, clean_lines, line_number, file_extensi continue if not current_indentation_level: if not (in_preprocessor_directive or looking_for_semicolon): - if not match(r'\S', current_line): + if not match(r'\S', current_line) and not file_state.did_inside_namespace_indent_warning(): + file_state.set_did_inside_namespace_indent_warning() error(filename, line_number + line_offset, 'whitespace/indent', 4, 'Code inside a namespace should not be indented.') if in_preprocessor_directive or (current_line.strip()[0] == '#'): # This takes care of preprocessor directive syntax. @@ -1871,7 +1901,8 @@ def check_braces(filename, clean_lines, line_number, error): 'This { should be at the end of the previous line') elif (search(r'\)\s*(const\s*)?{\s*$', line) and line.count('(') == line.count(')') - and not search(r'\b(if|for|foreach|while|switch)\b', line)): + and not search(r'\b(if|for|foreach|while|switch)\b', line) + and not match(r'\s+[A-Z_][A-Z_0-9]+\b', line)): error(filename, line_number, 'whitespace/braces', 4, 'Place brace on its own line for function definitions.') @@ -2124,7 +2155,7 @@ def get_line_width(line): return len(line) -def check_style(filename, clean_lines, line_number, file_extension, error): +def check_style(filename, clean_lines, line_number, file_extension, file_state, error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we @@ -2136,6 +2167,8 @@ def check_style(filename, clean_lines, line_number, file_extension, error): clean_lines: A CleansedLines instance containing the file. line_number: The number of the line to check. file_extension: The extension (without the dot) of the filename. + file_state: A _FileState instance which maintains information about + the state of things in the file. error: The function to call with any errors found. """ @@ -2203,7 +2236,7 @@ def check_style(filename, clean_lines, line_number, file_extension, error): 'operators on the left side of the line instead of the right side.') # Some more style checks - check_namespace_indentation(filename, clean_lines, line_number, file_extension, error) + check_namespace_indentation(filename, clean_lines, line_number, file_extension, file_state, error) check_using_std(filename, clean_lines, line_number, error) check_max_min_macros(filename, clean_lines, line_number, error) check_switch_indentation(filename, clean_lines, line_number, error) @@ -2309,7 +2342,7 @@ def _classify_include(filename, include, is_system, include_state): include_base = FileInfo(include).base_name() # If we haven't encountered a primary header, then be lenient in checking. - if not include_state.visited_primary_section() and target_base.startswith(include_base): + if not include_state.visited_primary_section() and target_base.find(include_base) != -1: return _PRIMARY_HEADER # If we already encountered a primary header, perform a strict comparison. # In case the two filename bases are the same then the above lenient check @@ -2616,6 +2649,109 @@ def check_language(filename, clean_lines, line_number, file_extension, include_s 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') + check_identifier_name_in_declaration(filename, line_number, line, error) + + +def check_identifier_name_in_declaration(filename, line_number, line, error): + """Checks if identifier names contain any underscores. + + As identifiers in libraries we are using have a bunch of + underscores, we only warn about the declarations of identifiers + and don't check use of identifiers. + + Args: + filename: The name of the current file. + line_number: The number of the line to check. + line: The line of code to check. + error: The function to call with any errors found. + """ + # We don't check a return statement. + if match(r'\s*return\b', line): + return + + # Basically, a declaration is a type name followed by whitespaces + # followed by an identifier. The type name can be complicated + # due to type adjectives and templates. We remove them first to + # simplify the process to find declarations of identifiers. + + # Convert "long long", "long double", and "long long int" to + # simple types, but don't remove simple "long". + line = sub(r'long (long )?(?=long|double|int)', '', line) + line = sub(r'\b(unsigned|signed|inline|using|static|const|volatile|auto|register|extern|typedef|restrict|struct|class|virtual)(?=\W)', '', line) + + # Remove all template parameters by removing matching < and >. + # Loop until no templates are removed to remove nested templates. + while True: + line, number_of_replacements = subn(r'<([\w\s:]|::)+\s*[*&]*\s*>', '', line) + if not number_of_replacements: + break + + # Declarations of local variables can be in condition expressions + # of control flow statements (e.g., "if (RenderObject* p = o->parent())"). + # We remove the keywords and the first parenthesis. + # + # Declarations in "while", "if", and "switch" are different from + # other declarations in two aspects: + # + # - There can be only one declaration between the parentheses. + # (i.e., you cannot write "if (int i = 0, j = 1) {}") + # - The variable must be initialized. + # (i.e., you cannot write "if (int i) {}") + # + # and we will need different treatments for them. + line = sub(r'^\s*for\s*\(', '', line) + line, control_statement = subn(r'^\s*(while|else if|if|switch)\s*\(', '', line) + + # Detect variable and functions. + type_regexp = r'\w([\w]|\s*[*&]\s*|::)+' + identifier_regexp = r'(?P<identifier>[\w:]+)' + character_after_identifier_regexp = r'(?P<character_after_identifier>[[;()=,])(?!=)' + declaration_without_type_regexp = r'\s*' + identifier_regexp + r'\s*' + character_after_identifier_regexp + declaration_with_type_regexp = r'\s*' + type_regexp + r'\s' + declaration_without_type_regexp + is_function_arguments = False + number_of_identifiers = 0 + while True: + # If we are seeing the first identifier or arguments of a + # function, there should be a type name before an identifier. + if not number_of_identifiers or is_function_arguments: + declaration_regexp = declaration_with_type_regexp + else: + declaration_regexp = declaration_without_type_regexp + + matched = match(declaration_regexp, line) + if not matched: + return + identifier = matched.group('identifier') + character_after_identifier = matched.group('character_after_identifier') + + # If we removed a non-for-control statement, the character after + # the identifier should be '='. With this rule, we can avoid + # warning for cases like "if (val & INT_MAX) {". + if control_statement and character_after_identifier != '=': + return + + is_function_arguments = is_function_arguments or character_after_identifier == '(' + + # Remove "m_" and "s_" to allow them. + modified_identifier = sub(r'(^|(?<=::))[ms]_', '', identifier) + if modified_identifier.find('_') >= 0: + # Various exceptions to the rule: JavaScript op codes functions, const_iterator. + if (not (filename.find('JavaScriptCore') >= 0 and modified_identifier.find('_op_') >= 0) + and not modified_identifier == "const_iterator"): + error(filename, line_number, 'readability/naming', 4, identifier + " is incorrectly named. Don't use underscores in your identifier names.") + + # There can be only one declaration in non-for-control statements. + if control_statement: + return + # We should continue checking if this is a function + # declaration because we need to check its arguments. + # Also, we need to check multiple declarations. + if character_after_identifier != '(' and character_after_identifier != ',': + return + + number_of_identifiers += 1 + line = line[matched.end():] + def check_c_style_cast(filename, line_number, line, raw_line, cast_type, pattern, error): @@ -2914,7 +3050,7 @@ def check_for_include_what_you_use(filename, clean_lines, include_state, error, def process_line(filename, file_extension, clean_lines, line, include_state, function_state, - class_state, error): + class_state, file_state, error): """Processes a single line in the file. Args: @@ -2927,6 +3063,8 @@ def process_line(filename, file_extension, function_state: A _FunctionState instance which counts function lines, etc. class_state: A _ClassState instance which maintains information about the current stack of nested class declarations being parsed. + file_state: A _FileState instance which maintains information about + the state of things in the file. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message @@ -2936,7 +3074,7 @@ def process_line(filename, file_extension, if search(r'\bNOLINT\b', raw_lines[line]): # ignore nolint lines return check_for_multiline_comments_and_strings(filename, clean_lines, line, error) - check_style(filename, clean_lines, line, file_extension, error) + check_style(filename, clean_lines, line, file_extension, file_state, error) check_language(filename, clean_lines, line, file_extension, include_state, error) check_for_non_standard_constructs(filename, clean_lines, line, @@ -2961,6 +3099,7 @@ def process_file_data(filename, file_extension, lines, error): include_state = _IncludeState() function_state = _FunctionState() class_state = _ClassState() + file_state = _FileState() check_for_copyright(filename, lines, error) @@ -2971,7 +3110,7 @@ def process_file_data(filename, file_extension, lines, error): clean_lines = CleansedLines(lines) for line in xrange(clean_lines.num_lines()): process_line(filename, file_extension, clean_lines, line, - include_state, function_state, class_state, error) + include_state, function_state, class_state, file_state, error) class_state.check_finished(filename, error) check_for_include_what_you_use(filename, clean_lines, include_state, error) @@ -3038,8 +3177,6 @@ def process_file(filename, error=error): 'One or more unexpected \\r (^M) found;' 'better to use only a \\n') - sys.stderr.write('Done processing %s\n' % filename) - def print_usage(message): """Prints a brief usage string and exits, optionally with an error message. diff --git a/WebKitTools/Scripts/modules/cpp_style_unittest.py b/WebKitTools/Scripts/modules/cpp_style_unittest.py index d5637f4..75dd47e 100644 --- a/WebKitTools/Scripts/modules/cpp_style_unittest.py +++ b/WebKitTools/Scripts/modules/cpp_style_unittest.py @@ -120,9 +120,10 @@ class CppStyleTestBase(unittest.TestCase): function_state = cpp_style._FunctionState() ext = file_name[file_name.rfind('.') + 1:] class_state = cpp_style._ClassState() + file_state = cpp_style._FileState() cpp_style.process_line(file_name, ext, clean_lines, 0, include_state, function_state, - class_state, error_collector) + class_state, file_state, error_collector) # Single-line lint tests are allowed to fail the 'unlintable function' # check. error_collector.remove_if_present( @@ -137,8 +138,9 @@ class CppStyleTestBase(unittest.TestCase): lines = cpp_style.CleansedLines(lines) ext = file_name[file_name.rfind('.') + 1:] class_state = cpp_style._ClassState() + file_state = cpp_style._FileState() for i in xrange(lines.num_lines()): - cpp_style.check_style(file_name, lines, i, ext, error_collector) + cpp_style.check_style(file_name, lines, i, ext, file_state, error_collector) cpp_style.check_for_non_standard_constructs(file_name, lines, i, class_state, error_collector) class_state.check_finished(file_name, error_collector) @@ -934,15 +936,15 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('int doublesize[some_var * 2];', errmsg) self.assert_lint('int a[afunction()];', errmsg) self.assert_lint('int a[function(kMaxFooBars)];', errmsg) - self.assert_lint('bool a_list[items_->size()];', errmsg) + self.assert_lint('bool aList[items_->size()];', errmsg) self.assert_lint('namespace::Type buffer[len+1];', errmsg) self.assert_lint('int a[64];', '') self.assert_lint('int a[0xFF];', '') self.assert_lint('int first[256], second[256];', '') - self.assert_lint('int array_name[kCompileTimeConstant];', '') + self.assert_lint('int arrayName[kCompileTimeConstant];', '') self.assert_lint('char buf[somenamespace::kBufSize];', '') - self.assert_lint('int array_name[ALL_CAPS];', '') + self.assert_lint('int arrayName[ALL_CAPS];', '') self.assert_lint('AClass array1[foo::bar::ALL_CAPS];', '') self.assert_lint('int a[kMaxStrLen + 1];', '') self.assert_lint('int a[sizeof(foo)];', '') @@ -1116,6 +1118,12 @@ class CppStyleTest(CppStyleTestBase): 'if (condition) {', '') self.assert_multi_line_lint( + ' MACRO1(macroArg) {', + '') + self.assert_multi_line_lint( + 'ACCESSOR_GETTER(MessageEventPorts) {', + 'Place brace on its own line for function definitions. [whitespace/braces] [4]') + self.assert_multi_line_lint( 'int foo() {', 'Place brace on its own line for function definitions. [whitespace/braces] [4]') self.assert_multi_line_lint( @@ -1271,6 +1279,8 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('a = 1 << 20', '') self.assert_multi_line_lint('#include "config.h"\n#include <sys/io.h>\n', '') + self.assert_multi_line_lint('#include "config.h"\n#import <foo/bar.h>\n', + '') def test_spacing_before_last_semicolon(self): self.assert_lint('call_function() ;', @@ -1520,14 +1530,14 @@ class CppStyleTest(CppStyleTestBase): def test_indent(self): self.assert_lint('static int noindent;', '') - self.assert_lint(' int four_space_indent;', '') - self.assert_lint(' int one_space_indent;', + self.assert_lint(' int fourSpaceIndent;', '') + self.assert_lint(' int oneSpaceIndent;', 'Weird number of spaces at line-start. ' 'Are you using a 4-space indent? [whitespace/indent] [3]') - self.assert_lint(' int three_space_indent;', + self.assert_lint(' int threeSpaceIndent;', 'Weird number of spaces at line-start. ' 'Are you using a 4-space indent? [whitespace/indent] [3]') - self.assert_lint(' char* one_space_indent = "public:";', + self.assert_lint(' char* oneSpaceIndent = "public:";', 'Weird number of spaces at line-start. ' 'Are you using a 4-space indent? [whitespace/indent] [3]') self.assert_lint(' public:', '') @@ -1960,7 +1970,7 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('double const static foo = 2.0;', build_storage_class_error_message) - self.assert_lint('uint64 typedef unsigned_long_long;', + self.assert_lint('uint64 typedef unsignedLongLong;', build_storage_class_error_message) self.assert_lint('int register foo = 0;', @@ -2044,6 +2054,7 @@ class CppStyleTest(CppStyleTestBase): 'Changing pointer instead of value (or unused value of ' 'operator*). [runtime/invalid_increment] [5]') + class CleansedLinesTest(unittest.TestCase): def test_init(self): lines = ['Line 1', @@ -2305,6 +2316,10 @@ class OrderOfIncludesTest(CppStyleTestBase): classify_include('fooCustom.cpp', 'foo.h', False, include_state)) + self.assertEqual(cpp_style._PRIMARY_HEADER, + classify_include('PrefixFooCustom.cpp', + 'Foo.h', + False, include_state)) # Tricky example where both includes might be classified as primary. self.assert_language_rules_check('ScrollbarThemeWince.cpp', '#include "config.h"\n' @@ -2828,7 +2843,16 @@ class WebKitStyleTest(CppStyleTestBase): '};\n' '};\n' '}', - ['Code inside a namespace should not be indented. [whitespace/indent] [4]', 'namespace should never be indented. [whitespace/indent] [4]'], + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.h') + self.assert_multi_line_lint( + 'namespace OuterNamespace {\n' + ' class Document {\n' + ' namespace InnerNamespace {\n' + '};\n' + '};\n' + '}', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', 'foo.h') self.assert_multi_line_lint( 'namespace WebCore {\n' @@ -3584,8 +3608,104 @@ class WebKitStyleTest(CppStyleTestBase): 'foo.h') def test_names(self): - # FIXME: Implement this. - pass + name_error_message = " is incorrectly named. Don't use underscores in your identifier names. [readability/naming] [4]" + + # Basic cases from WebKit style guide. + self.assert_lint('struct Data;', '') + self.assert_lint('size_t bufferSize;', '') + self.assert_lint('class HTMLDocument;', '') + self.assert_lint('String mimeType();', '') + self.assert_lint('size_t buffer_size;', + 'buffer_size' + name_error_message) + self.assert_lint('short m_length;', '') + self.assert_lint('short _length;', + '_length' + name_error_message) + self.assert_lint('short length_;', + 'length_' + name_error_message) + + # Pointers, references, functions, templates, and adjectives. + self.assert_lint('char* under_score;', + 'under_score' + name_error_message) + self.assert_lint('const int UNDER_SCORE;', + 'UNDER_SCORE' + name_error_message) + self.assert_lint('static inline const char const& const under_score;', + 'under_score' + name_error_message) + self.assert_lint('WebCore::RenderObject* under_score;', + 'under_score' + name_error_message) + self.assert_lint('int func_name();', + 'func_name' + name_error_message) + self.assert_lint('RefPtr<RenderObject*> under_score;', + 'under_score' + name_error_message) + self.assert_lint('WTF::Vector<WTF::RefPtr<const RenderObject* const> > under_score;', + 'under_score' + name_error_message) + self.assert_lint('int under_score[];', + 'under_score' + name_error_message) + self.assert_lint('struct dirent* under_score;', + 'under_score' + name_error_message) + self.assert_lint('long under_score;', + 'under_score' + name_error_message) + self.assert_lint('long long under_score;', + 'under_score' + name_error_message) + self.assert_lint('long double under_score;', + 'under_score' + name_error_message) + self.assert_lint('long long int under_score;', + 'under_score' + name_error_message) + + # Declarations in control statement. + self.assert_lint('if (int under_score = 42) {', + 'under_score' + name_error_message) + self.assert_lint('else if (int under_score = 42) {', + 'under_score' + name_error_message) + self.assert_lint('for (int under_score = 42; cond; i++) {', + 'under_score' + name_error_message) + self.assert_lint('while (foo & under_score = bar) {', + 'under_score' + name_error_message) + self.assert_lint('for (foo * under_score = p; cond; i++) {', + 'under_score' + name_error_message) + self.assert_lint('for (foo * under_score; cond; i++) {', + 'under_score' + name_error_message) + self.assert_lint('while (foo & value_in_thirdparty_library) {', '') + self.assert_lint('while (foo * value_in_thirdparty_library) {', '') + self.assert_lint('if (mli && S_OK == mli->foo()) {', '') + + # More member variables and functions. + self.assert_lint('int SomeClass::s_validName', '') + self.assert_lint('int m_under_score;', + 'm_under_score' + name_error_message) + self.assert_lint('int SomeClass::s_under_score = 0;', + 'SomeClass::s_under_score' + name_error_message) + self.assert_lint('int SomeClass::under_score = 0;', + 'SomeClass::under_score' + name_error_message) + + # Other statements. + self.assert_lint('return INT_MAX;', '') + self.assert_lint('return_t under_score;', + 'under_score' + name_error_message) + self.assert_lint('goto under_score;', + 'under_score' + name_error_message) + + # Multiple variables in one line. + self.assert_lint('void myFunction(int variable1, int another_variable);', + 'another_variable' + name_error_message) + self.assert_lint('int variable1, another_variable;', + 'another_variable' + name_error_message) + self.assert_lint('int first_variable, secondVariable;', + 'first_variable' + name_error_message) + self.assert_lint('void my_function(int variable_1, int variable_2);', + ['my_function' + name_error_message, + 'variable_1' + name_error_message, + 'variable_2' + name_error_message]) + self.assert_lint('for (int variable_1, variable_2;;) {', + ['variable_1' + name_error_message, + 'variable_2' + name_error_message]) + + # There is an exception for op code functions but only in the JavaScriptCore directory. + self.assert_lint('void this_op_code(int var1, int var2)', '', 'JavaScriptCore/foo.cpp') + self.assert_lint('void this_op_code(int var1, int var2)', 'this_op_code' + name_error_message) + + # const_iterator is allowed as well. + self.assert_lint('typedef VectorType::const_iterator const_iterator;', '') + def test_other(self): # FIXME: Implement this. diff --git a/WebKitTools/Scripts/modules/executive.py b/WebKitTools/Scripts/modules/executive.py new file mode 100644 index 0000000..b73e17d --- /dev/null +++ b/WebKitTools/Scripts/modules/executive.py @@ -0,0 +1,124 @@ +# 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. + +import os +import StringIO +import subprocess +import sys + +from modules.logging import tee + + +class ScriptError(Exception): + def __init__(self, message=None, script_args=None, exit_code=None, output=None, cwd=None): + if not message: + message = 'Failed to run "%s"' % script_args + if exit_code: + message += " exit_code: %d" % exit_code + if cwd: + message += " cwd: %s" % cwd + + Exception.__init__(self, message) + self.script_args = script_args # 'args' is already used by Exception + self.exit_code = exit_code + self.output = output + self.cwd = cwd + + def message_with_output(self, output_limit=500): + if self.output: + if output_limit and len(self.output) > output_limit: + return "%s\nLast %s characters of output:\n%s" % (self, output_limit, self.output[-output_limit:]) + return "%s\n%s" % (self, self.output) + return str(self) + + +# FIXME: This should not be a global static. +# New code should use Executive.run_command directly instead +def run_command(*args, **kwargs): + return Executive().run_command(*args, **kwargs) + + +class Executive(object): + def _run_command_with_teed_output(self, args, teed_output): + child_process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + # Use our own custom wait loop because Popen ignores a tee'd stderr/stdout. + # FIXME: This could be improved not to flatten output to stdout. + while True: + output_line = child_process.stdout.readline() + if output_line == "" and child_process.poll() != None: + return child_process.poll() + teed_output.write(output_line) + + 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() + if quiet: + dev_null = open(os.devnull, "w") + child_stdout = tee(child_out_file, dev_null if quiet else sys.stdout) + exit_code = self._run_command_with_teed_output(args, child_stdout) + if quiet: + dev_null.close() + + child_output = child_out_file.getvalue() + child_out_file.close() + + if exit_code: + raise ScriptError(script_args=args, exit_code=exit_code, output=child_output) + + # Error handlers do not need to be static methods once all callers are updated to use an Executive object. + @staticmethod + def default_error_handler(error): + raise error + + @staticmethod + def ignore_error(error): + pass + + # FIXME: This should be merged with run_and_throw_if_fail + def run_command(self, args, cwd=None, input=None, error_handler=None, return_exit_code=False, return_stderr=True): + if hasattr(input, 'read'): # Check if the input is a file. + stdin = input + string_to_communicate = None + else: + stdin = subprocess.PIPE if input else None + string_to_communicate = input + if return_stderr: + stderr = subprocess.STDOUT + else: + stderr = None + process = subprocess.Popen(args, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, cwd=cwd) + output = process.communicate(string_to_communicate)[0] + exit_code = process.wait() + 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/modules/grammar.py b/WebKitTools/Scripts/modules/grammar.py new file mode 100644 index 0000000..dd2967a --- /dev/null +++ b/WebKitTools/Scripts/modules/grammar.py @@ -0,0 +1,43 @@ +#!/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. + +import re + +def plural(noun): + # This is a dumb plural() implementation which was just enough for our uses. + if re.search("h$", noun): + return noun + "es" + else: + return noun + "s" + +def pluralize(noun, count): + if count != 1: + noun = plural(noun) + return "%d %s" % (count, noun) diff --git a/WebKitTools/Scripts/modules/landingsequence.py b/WebKitTools/Scripts/modules/landingsequence.py new file mode 100644 index 0000000..90683f4 --- /dev/null +++ b/WebKitTools/Scripts/modules/landingsequence.py @@ -0,0 +1,113 @@ +#!/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 modules.comments import bug_comment_from_commit_text +from modules.executive import ScriptError +from modules.logging import log +from modules.scm import CheckoutNeedsUpdate +from modules.webkitport import WebKitPort +from modules.workqueue import WorkQueue +from modules.buildsteps import CleanWorkingDirectoryStep, UpdateStep, ApplyPatchStep, EnsureBuildersAreGreenStep, BuildStep, RunTestsStep, CommitStep, ClosePatchStep, CloseBugStep + + +class LandingSequenceErrorHandler(): + @classmethod + def handle_script_error(cls, tool, patch, script_error): + raise NotImplementedError, "subclasses must implement" + +# FIXME: This class is slowing being killed and replaced with StepSequence. +class LandingSequence: + def __init__(self, patch, options, tool): + self._patch = patch + self._options = options + self._tool = tool + self._port = WebKitPort.port(self._options.port) + + def run(self): + self.clean() + self.update() + self.apply_patch() + self.check_builders() + self.build() + self.test() + commit_log = self.commit() + self.close_patch(commit_log) + self.close_bug() + + def run_and_handle_errors(self): + try: + self.run() + 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.") + WorkQueue.exit_after_handled_error(e) + except ScriptError, e: + if not self._options.quiet: + log(e.message_with_output()) + if self._options.parent_command: + command = self._tool.command_by_name(self._options.parent_command) + command.handle_script_error(self._tool, self._patch, e) + WorkQueue.exit_after_handled_error(e) + + def clean(self): + step = CleanWorkingDirectoryStep(self._tool, self._options) + step.run() + + def update(self): + step = UpdateStep(self._tool, self._options) + step.run() + + def apply_patch(self): + step = ApplyPatchStep(self._tool, self._options, self._patch) + step.run() + + def check_builders(self): + step = EnsureBuildersAreGreenStep(self._tool, self._options) + step.run() + + def build(self): + step = BuildStep(self._tool, self._options) + step.run() + + def test(self): + step = RunTestsStep(self._tool, self._options) + step.run() + + def commit(self): + step = CommitStep(self._tool, self._options) + return step.run() + + def close_patch(self, commit_log): + step = ClosePatchStep(self._tool, self._options, self._patch) + step.run(commit_log) + + def close_bug(self): + step = CloseBugStep(self._tool, self._options, self._patch) + step.run() diff --git a/WebKitTools/Scripts/modules/logging.py b/WebKitTools/Scripts/modules/logging.py index cbccacf..7b7cec5 100644 --- a/WebKitTools/Scripts/modules/logging.py +++ b/WebKitTools/Scripts/modules/logging.py @@ -29,6 +29,7 @@ # # WebKit's Python module for logging +import os import sys def log(string): @@ -46,3 +47,38 @@ class tee: def write(self, string): for file in self.files: file.write(string) + +class OutputTee: + def __init__(self): + self._original_stdout = None + self._original_stderr = None + self._files_for_output = [] + + def add_log(self, path): + log_file = self._open_log_file(path) + self._files_for_output.append(log_file) + self._tee_outputs_to_files(self._files_for_output) + return log_file + + def remove_log(self, log_file): + self._files_for_output.remove(log_file) + self._tee_outputs_to_files(self._files_for_output) + log_file.close() + + @staticmethod + def _open_log_file(log_path): + (log_directory, log_name) = os.path.split(log_path) + if log_directory and not os.path.exists(log_directory): + os.makedirs(log_directory) + return open(log_path, 'a+') + + def _tee_outputs_to_files(self, files): + if not self._original_stdout: + self._original_stdout = sys.stdout + self._original_stderr = sys.stderr + if files and len(files): + sys.stdout = tee(self._original_stdout, *files) + sys.stderr = tee(self._original_stderr, *files) + else: + sys.stdout = self._original_stdout + sys.stderr = self._original_stderr diff --git a/WebKitTools/Scripts/modules/logging_unittest.py b/WebKitTools/Scripts/modules/logging_unittest.py index 7d41e56..b09a563 100644 --- a/WebKitTools/Scripts/modules/logging_unittest.py +++ b/WebKitTools/Scripts/modules/logging_unittest.py @@ -32,8 +32,8 @@ import StringIO import tempfile import unittest +from modules.executive import ScriptError from modules.logging import * -from modules.scm import ScriptError class LoggingTest(unittest.TestCase): diff --git a/WebKitTools/Scripts/modules/mock.py b/WebKitTools/Scripts/modules/mock.py new file mode 100644 index 0000000..f6f328e --- /dev/null +++ b/WebKitTools/Scripts/modules/mock.py @@ -0,0 +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)
diff --git a/WebKitTools/Scripts/modules/mock_bugzillatool.py b/WebKitTools/Scripts/modules/mock_bugzillatool.py new file mode 100644 index 0000000..e600947 --- /dev/null +++ b/WebKitTools/Scripts/modules/mock_bugzillatool.py @@ -0,0 +1,153 @@ +# 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 modules.mock import Mock +from modules.scm import CommitMessage + + +class MockBugzilla(Mock): + patch1 = { + "id" : 197, + "bug_id" : 42, + "url" : "http://example.com/197", + "is_obsolete" : False, + "reviewer" : "Reviewer1", + "attacher_email" : "Contributer1", + } + patch2 = { + "id" : 128, + "bug_id" : 42, + "url" : "http://example.com/128", + "is_obsolete" : False, + "reviewer" : "Reviewer2", + "attacher_email" : "Contributer2", + } + bug_server_url = "http://example.com" + + def fetch_bug_ids_from_commit_queue(self): + return [42, 75] + + def fetch_attachment_ids_from_review_queue(self): + return [197, 128] + + def fetch_patches_from_commit_queue(self): + return [self.patch1, self.patch2] + + def fetch_patches_from_pending_commit_list(self): + return [self.patch1, self.patch2] + + def fetch_reviewed_patches_from_bug(self, bug_id): + if bug_id == 42: + return [self.patch1, self.patch2] + return None + + def fetch_attachments_from_bug(self, bug_id): + if bug_id == 42: + return [self.patch1, self.patch2] + return None + + def fetch_patches_from_bug(self, bug_id): + if bug_id == 42: + return [self.patch1, self.patch2] + return None + + def fetch_attachment(self, attachment_id): + if attachment_id == 197: + return self.patch1 + if attachment_id == 128: + return self.patch2 + raise Exception("Bogus attachment_id in fetch_attachment.") + + def bug_url_for_bug_id(self, bug_id): + return "%s/%s" % (self.bug_server_url, bug_id) + + def attachment_url_for_id(self, attachment_id, action): + action_param = "" + if action and action != "view": + action_param = "&action=%s" % action + return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param) + + +class MockBuildBot(Mock): + def builder_statuses(self): + return [{ + "name": "Builder1", + "is_green": True + }, { + "name": "Builder2", + "is_green": True + }] + + def red_core_builders_names(self): + return [] + +class MockSCM(Mock): + def __init__(self): + Mock.__init__(self) + self.checkout_root = os.getcwd() + + def create_patch(self): + return "Patch1" + + def commit_ids_from_commitish_arguments(self, args): + return ["Commitish1", "Commitish2"] + + def commit_message_for_local_commit(self, commit_id): + if commit_id == "Commitish1": + return CommitMessage("CommitMessage1\nhttps://bugs.example.org/show_bug.cgi?id=42\n") + if commit_id == "Commitish2": + return CommitMessage("CommitMessage2\nhttps://bugs.example.org/show_bug.cgi?id=75\n") + raise Exception("Bogus commit_id in commit_message_for_local_commit.") + + def create_patch_from_local_commit(self, commit_id): + if commit_id == "Commitish1": + return "Patch1" + if commit_id == "Commitish2": + return "Patch2" + raise Exception("Bogus commit_id in commit_message_for_local_commit.") + + 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! + return [] + + +class MockBugzillaTool(): + def __init__(self): + self.bugs = MockBugzilla() + self.buildbot = MockBuildBot() + self.executive = Mock() + self._scm = MockSCM() + + def scm(self): + return self._scm + + def path(self): + return "echo" diff --git a/WebKitTools/Scripts/modules/multicommandtool.py b/WebKitTools/Scripts/modules/multicommandtool.py new file mode 100644 index 0000000..0475cf1 --- /dev/null +++ b/WebKitTools/Scripts/modules/multicommandtool.py @@ -0,0 +1,253 @@ +# 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. +# +# MultiCommandTool provides a framework for writing svn-like/git-like tools +# which are called with the following format: +# tool-name [global options] command-name [command options] + +import sys + +from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option + +from modules.grammar import pluralize +from modules.logging import log + +class Command(object): + name = None + # show_in_main_help = False # Subclasses must define show_in_main_help, we leave it out here to enforce that. + def __init__(self, help_text, argument_names=None, options=None, requires_local_commits=False): + self.help_text = help_text + self.argument_names = argument_names + self.required_arguments = self._parse_required_arguments(argument_names) + self.options = options + self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options) + self.requires_local_commits = requires_local_commits + self.tool = None + + # The tool calls bind_to_tool on each Command after adding it to its list. + def bind_to_tool(self, tool): + # Command instances can only be bound to one tool at a time. + if self.tool and tool != self.tool: + raise Exception("Command already bound to tool!") + self.tool = tool + + @staticmethod + def _parse_required_arguments(argument_names): + required_args = [] + if not argument_names: + return required_args + split_args = argument_names.split(" ") + for argument in split_args: + if argument[0] == '[': + # For now our parser is rather dumb. Do some minimal validation that + # we haven't confused it. + if argument[-1] != ']': + raise Exception("Failure to parse argument string %s. Argument %s is missing ending ]" % (argument_names, argument)) + else: + required_args.append(argument) + return required_args + + def name_with_arguments(self): + usage_string = self.name + if self.options: + usage_string += " [options]" + if self.argument_names: + usage_string += " " + self.argument_names + return usage_string + + def parse_args(self, args): + return self.option_parser.parse_args(args) + + def check_arguments_and_execute(self, args_after_command_name, tool): + (command_options, command_args) = self.parse_args(args_after_command_name) + + if len(command_args) < len(self.required_arguments): + log("%s required, %s provided. Provided: %s Required: %s\nSee '%s help %s' for usage." % ( + pluralize("argument", len(self.required_arguments)), + pluralize("argument", len(command_args)), + "'%s'" % " ".join(command_args), + " ".join(self.required_arguments), + tool.name(), + self.name)) + return 1 + return self.execute(command_options, command_args, tool) or 0 + + def standalone_help(self): + help_text = self.name_with_arguments().ljust(len(self.name_with_arguments()) + 3) + self.help_text + "\n" + help_text += self.option_parser.format_option_help(IndentedHelpFormatter()) + return help_text + + def execute(self, options, args, tool): + raise NotImplementedError, "subclasses must implement" + + +class HelpPrintingOptionParser(OptionParser): + def __init__(self, epilog_method=None, *args, **kwargs): + self.epilog_method = epilog_method + OptionParser.__init__(self, *args, **kwargs) + + def error(self, msg): + self.print_usage(sys.stderr) + error_message = "%s: error: %s\n" % (self.get_prog_name(), msg) + error_message += "\nType \"%s --help\" to see usage.\n" % self.get_prog_name() + self.exit(1, error_message) + + # We override format_epilog to avoid the default formatting which would paragraph-wrap the epilog + # and also to allow us to compute the epilog lazily instead of in the constructor (allowing it to be context sensitive). + def format_epilog(self, epilog): + if self.epilog_method: + return "\n%s\n" % self.epilog_method() + return "" + + +class HelpCommand(Command): + name = "help" + show_in_main_help = False + + def __init__(self): + options = [ + make_option("-a", "--all-commands", action="store_true", dest="show_all_commands", help="Print all available commands"), + ] + Command.__init__(self, "Display information about this program or its subcommands", "[COMMAND]", options=options) + self.show_all_commands = False # A hack used to pass --all-commands to _help_epilog even though it's called by the OptionParser. + + def _help_epilog(self): + # Only show commands which are relevant to this checkout's SCM system. Might this be confusing to some users? + if self.show_all_commands: + epilog = "All %prog commands:\n" + relevant_commands = self.tool.commands[:] + else: + epilog = "Common %prog commands:\n" + relevant_commands = filter(self.tool.should_show_in_main_help, self.tool.commands) + longest_name_length = max(map(lambda command: len(command.name), relevant_commands)) + relevant_commands.sort(lambda a, b: cmp(a.name, b.name)) + command_help_texts = map(lambda command: " %s %s\n" % (command.name.ljust(longest_name_length), command.help_text), relevant_commands) + epilog += "%s\n" % "".join(command_help_texts) + epilog += "See '%prog help --all-commands' to list all commands.\n" + epilog += "See '%prog help COMMAND' for more information on a specific command.\n" + return self.tool.global_option_parser.expand_prog_name(epilog) + + def execute(self, options, args, tool): + if args: + command = self.tool.command_by_name(args[0]) + if command: + print command.standalone_help() + return 0 + + self.show_all_commands = options.show_all_commands + tool.global_option_parser.print_help() + return 0 + + +class MultiCommandTool(object): + def __init__(self, name=None, commands=None): + # Allow the unit tests to disable command auto-discovery. + self.commands = commands or [cls() for cls in self._find_all_commands() if cls.name] + self.help_command = self.command_by_name(HelpCommand.name) + # Require a help command, even if the manual test list doesn't include one. + if not self.help_command: + self.help_command = HelpCommand() + self.commands.append(self.help_command) + for command in self.commands: + command.bind_to_tool(self) + self.global_option_parser = HelpPrintingOptionParser(epilog_method=self.help_command._help_epilog, prog=name, usage=self._usage_line()) + + @classmethod + def _add_all_subclasses(cls, class_to_crawl, seen_classes): + for subclass in class_to_crawl.__subclasses__(): + if subclass not in seen_classes: + seen_classes.add(subclass) + cls._add_all_subclasses(subclass, seen_classes) + + @classmethod + def _find_all_commands(cls): + commands = set() + cls._add_all_subclasses(Command, commands) + return sorted(commands) + + @staticmethod + def _usage_line(): + return "Usage: %prog [options] COMMAND [ARGS]" + + def name(self): + return self.global_option_parser.get_prog_name() + + def handle_global_args(self, args): + (options, args) = self.global_option_parser.parse_args(args) + # We should never hit this because _split_args splits at the first arg without a leading "-". + if args: + self.global_option_parser.error("Extra arguments before command: %s" % args) + + @staticmethod + def _split_args(args): + # Assume the first argument which doesn't start with "-" is the command name. + command_index = 0 + for arg in args: + if arg[0] != "-": + break + command_index += 1 + else: + return (args[:], None, []) + + global_args = args[:command_index] + command = args[command_index] + command_args = args[command_index + 1:] + return (global_args, command, command_args) + + def command_by_name(self, command_name): + for command in self.commands: + if command_name == command.name: + return command + return None + + def path(self): + raise NotImplementedError, "subclasses must implement" + + def should_show_in_main_help(self, command): + return command.show_in_main_help + + def should_execute_command(self, command): + raise NotImplementedError, "subclasses must implement" + + def main(self, argv=sys.argv): + (global_args, command_name, args_after_command_name) = self._split_args(argv[1:]) + + # Handle --help, etc: + self.handle_global_args(global_args) + + command = self.command_by_name(command_name) or self.help_command + if not command: + self.global_option_parser.error("%s is not a recognized command" % command_name) + + (should_execute, failure_reason) = self.should_execute_command(command) + if not should_execute: + log(failure_reason) + return 0 + + return command.check_arguments_and_execute(args_after_command_name, self) diff --git a/WebKitTools/Scripts/modules/multicommandtool_unittest.py b/WebKitTools/Scripts/modules/multicommandtool_unittest.py new file mode 100644 index 0000000..c71cc09 --- /dev/null +++ b/WebKitTools/Scripts/modules/multicommandtool_unittest.py @@ -0,0 +1,158 @@ +# 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 sys +import unittest +from multicommandtool import MultiCommandTool, Command +from modules.outputcapture import OutputCapture + +from optparse import make_option + +class TrivialCommand(Command): + name = "trivial" + show_in_main_help = True + def __init__(self, **kwargs): + Command.__init__(self, "help text", **kwargs) + + def execute(self, options, args, tool): + pass + +class UncommonCommand(TrivialCommand): + name = "uncommon" + show_in_main_help = False + +class CommandTest(unittest.TestCase): + def test_name_with_arguments(self): + command_with_args = TrivialCommand(argument_names="ARG1 ARG2") + self.assertEqual(command_with_args.name_with_arguments(), "trivial ARG1 ARG2") + + command_with_args = TrivialCommand(options=[make_option("--my_option")]) + self.assertEqual(command_with_args.name_with_arguments(), "trivial [options]") + + def test_parse_required_arguments(self): + self.assertEqual(Command._parse_required_arguments("ARG1 ARG2"), ["ARG1", "ARG2"]) + self.assertEqual(Command._parse_required_arguments("[ARG1] [ARG2]"), []) + self.assertEqual(Command._parse_required_arguments("[ARG1] ARG2"), ["ARG2"]) + # Note: We might make our arg parsing smarter in the future and allow this type of arguments string. + self.assertRaises(Exception, Command._parse_required_arguments, "[ARG1 ARG2]") + + def test_required_arguments(self): + two_required_arguments = TrivialCommand(argument_names="ARG1 ARG2 [ARG3]") + capture = OutputCapture() + capture.capture_output() + exit_code = two_required_arguments.check_arguments_and_execute(["foo"], TrivialTool()) + (stdout_string, stderr_string) = capture.restore_output() + expected_missing_args_error = "2 arguments required, 1 argument provided. Provided: 'foo' Required: ARG1 ARG2\nSee 'trivial-tool help trivial' for usage.\n" + self.assertEqual(exit_code, 1) + self.assertEqual(stdout_string, "") + self.assertEqual(stderr_string, expected_missing_args_error) + + +class TrivialTool(MultiCommandTool): + def __init__(self, commands=None): + MultiCommandTool.__init__(self, name="trivial-tool", commands=commands) + + def path(): + return __file__ + + def should_execute_command(self, command): + return (True, None) + + +class MultiCommandToolTest(unittest.TestCase): + def _assert_split(self, args, expected_split): + self.assertEqual(MultiCommandTool._split_args(args), expected_split) + + def test_split_args(self): + # MultiCommandToolTest._split_args returns: (global_args, command, command_args) + full_args = ["--global-option", "command", "--option", "arg"] + full_args_expected = (["--global-option"], "command", ["--option", "arg"]) + self._assert_split(full_args, full_args_expected) + + full_args = [] + full_args_expected = ([], None, []) + self._assert_split(full_args, full_args_expected) + + full_args = ["command", "arg"] + full_args_expected = ([], "command", ["arg"]) + self._assert_split(full_args, full_args_expected) + + def test_command_by_name(self): + # This also tests Command auto-discovery. + tool = TrivialTool() + self.assertEqual(tool.command_by_name("trivial").name, "trivial") + self.assertEqual(tool.command_by_name("bar"), None) + + def _assert_tool_main_outputs(self, tool, main_args, expected_stdout, expected_stderr = "", exit_code=0): + capture = OutputCapture() + capture.capture_output() + exit_code = tool.main(main_args) + (stdout_string, stderr_string) = capture.restore_output() + self.assertEqual(stdout_string, expected_stdout) + self.assertEqual(expected_stderr, expected_stderr) + + def test_global_help(self): + tool = TrivialTool(commands=[TrivialCommand(), UncommonCommand()]) + expected_common_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS] + +Options: + -h, --help show this help message and exit + +Common trivial-tool commands: + trivial help text + +See 'trivial-tool help --all-commands' to list all commands. +See 'trivial-tool help COMMAND' for more information on a specific command. + +""" + self._assert_tool_main_outputs(tool, ["tool", "help"], expected_common_commands_help) + expected_all_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS] + +Options: + -h, --help show this help message and exit + +All trivial-tool commands: + help Display information about this program or its subcommands + trivial help text + uncommon help text + +See 'trivial-tool help --all-commands' to list all commands. +See 'trivial-tool help COMMAND' for more information on a specific command. + +""" + self._assert_tool_main_outputs(tool, ["tool", "help", "--all-commands"], expected_all_commands_help) + + def test_command_help(self): + command_with_options = TrivialCommand(options=[make_option("--my_option")]) + tool = TrivialTool(commands=[command_with_options]) + expected_subcommand_help = "trivial [options] help text\nOptions:\n --my_option=MY_OPTION\n\n" + self._assert_tool_main_outputs(tool, ["tool", "help", "trivial"], expected_subcommand_help) + + +if __name__ == "__main__": + unittest.main() diff --git a/WebKitTools/Scripts/modules/outputcapture.py b/WebKitTools/Scripts/modules/outputcapture.py new file mode 100644 index 0000000..f02fc5d --- /dev/null +++ b/WebKitTools/Scripts/modules/outputcapture.py @@ -0,0 +1,53 @@ +# 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. +# +# Class for unittest support. Used for capturing stderr/stdout. + +import sys +from StringIO import StringIO + +class OutputCapture(object): + def __init__(self): + self.saved_outputs = dict() + + def _capture_output_with_name(self, output_name): + self.saved_outputs[output_name] = getattr(sys, output_name) + setattr(sys, output_name, StringIO()) + + def _restore_output_with_name(self, output_name): + captured_output = getattr(sys, output_name).getvalue() + setattr(sys, output_name, self.saved_outputs[output_name]) + del self.saved_outputs[output_name] + return captured_output + + def capture_output(self): + self._capture_output_with_name("stdout") + self._capture_output_with_name("stderr") + + def restore_output(self): + return (self._restore_output_with_name("stdout"), self._restore_output_with_name("stderr")) diff --git a/WebKitTools/Scripts/modules/patchcollection.py b/WebKitTools/Scripts/modules/patchcollection.py new file mode 100644 index 0000000..add8129 --- /dev/null +++ b/WebKitTools/Scripts/modules/patchcollection.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# 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. + +class PersistentPatchCollectionDelegate: + def collection_name(self): + raise NotImplementedError, "subclasses must implement" + + def fetch_potential_patch_ids(self): + raise NotImplementedError, "subclasses must implement" + + def status_server(self): + raise NotImplementedError, "subclasses must implement" + + +class PersistentPatchCollection: + _initial_status = "Pending" + _pass_status = "Pass" + _fail_status = "Fail" + def __init__(self, delegate): + self._delegate = delegate + self._name = self._delegate.collection_name() + self._status = self._delegate.status_server() + self._status_cache = {} + + def _cached_status(self, patch_id): + cached = self._status_cache.get(patch_id) + if cached: + return cached + status = self._status.patch_status(self._name, patch_id) + if status: + self._status_cache[patch_id] = status + return status + + def next(self): + patch_ids = self._delegate.fetch_potential_patch_ids() + for patch_id in patch_ids: + status = self._cached_status(patch_id) + if not status: + return patch_id + + def did_pass(self, patch): + self._status.update_status(self._name, self._pass_status, patch) + + def did_fail(self, patch): + self._status.update_status(self._name, self._fail_status, patch) diff --git a/WebKitTools/Scripts/modules/scm.py b/WebKitTools/Scripts/modules/scm.py index 3ffa23b..ff26693 100644 --- a/WebKitTools/Scripts/modules/scm.py +++ b/WebKitTools/Scripts/modules/scm.py @@ -34,6 +34,8 @@ import re import subprocess # Import WebKit-specific modules. +from modules.changelogs import ChangeLog +from modules.executive import Executive, run_command, ScriptError from modules.logging import error, log def detect_scm_system(path): @@ -77,44 +79,16 @@ class CommitMessage: return "\n".join(self.message_lines) + "\n" -class ScriptError(Exception): - def __init__(self, message=None, script_args=None, exit_code=None, output=None, cwd=None): - if not message: - message = 'Failed to run "%s"' % script_args - if exit_code: - message += " exit_code: %d" % exit_code - if cwd: - message += " cwd: %s" % cwd - - Exception.__init__(self, message) - self.script_args = script_args # 'args' is already used by Exception - self.exit_code = exit_code - self.output = output - self.cwd = cwd - - def message_with_output(self, output_limit=500): - if self.output: - if len(self.output) > output_limit: - return "%s\nLast %s characters of output:\n%s" % (self, output_limit, self.output[-output_limit:]) - return "%s\n%s" % (self, self.output) - return str(self) - - class CheckoutNeedsUpdate(ScriptError): def __init__(self, script_args, exit_code, output, cwd): ScriptError.__init__(self, script_args=script_args, exit_code=exit_code, output=output, cwd=cwd) -def default_error_handler(error): - raise error - def commit_error_handler(error): if re.search("resource out of date", error.output): raise CheckoutNeedsUpdate(script_args=error.script_args, exit_code=error.exit_code, output=error.output, cwd=error.cwd) - default_error_handler(error) + Executive.default_error_handler(error) -def ignore_error(error): - pass class SCM: def __init__(self, cwd, dryrun=False): @@ -122,33 +96,15 @@ class SCM: self.checkout_root = self.find_checkout_root(self.cwd) self.dryrun = dryrun - @staticmethod - def run_command(args, cwd=None, input=None, error_handler=default_error_handler, return_exit_code=False): - if hasattr(input, 'read'): # Check if the input is a file. - stdin = input - string_to_communicate = None - else: - stdin = subprocess.PIPE if input else None - string_to_communicate = input - process = subprocess.Popen(args, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) - output = process.communicate(string_to_communicate)[0].rstrip() - exit_code = process.wait() - if exit_code: - script_error = ScriptError(script_args=args, exit_code=exit_code, output=output, cwd=cwd) - error_handler(script_error) - if return_exit_code: - return exit_code - return output - def scripts_directory(self): return os.path.join(self.checkout_root, "WebKitTools", "Scripts") def script_path(self, script_name): return os.path.join(self.scripts_directory(), script_name) - def ensure_clean_working_directory(self, force): - if not force and not self.working_directory_is_clean(): - print self.run_command(self.status_command(), error_handler=ignore_error) + def ensure_clean_working_directory(self, force_clean): + if not force_clean and not self.working_directory_is_clean(): + 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.") log("Cleaning working directory") @@ -168,15 +124,17 @@ class SCM: # It's possible that the patch was not made from the root directory. # We should detect and handle that case. curl_process = subprocess.Popen(['curl', '--location', '--silent', '--show-error', patch['url']], stdout=subprocess.PIPE) - args = [self.script_path('svn-apply'), '--reviewer', patch['reviewer']] + args = [self.script_path('svn-apply')] + if patch.get('reviewer'): + args += ['--reviewer', patch['reviewer']] if force: args.append('--force') - self.run_command(args, input=curl_process.stdout) + run_command(args, input=curl_process.stdout) def run_status_and_extract_filenames(self, status_command, status_regexp): filenames = [] - for line in self.run_command(status_command).splitlines(): + for line in run_command(status_command).splitlines(): match = re.search(status_regexp, line) if not match: continue @@ -204,6 +162,28 @@ class SCM: 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" @@ -222,9 +202,6 @@ class SCM: def clean_working_directory(self): raise NotImplementedError, "subclasses must implement" - def update_webkit(self): - raise NotImplementedError, "subclasses must implement" - def status_command(self): raise NotImplementedError, "subclasses must implement" @@ -295,7 +272,7 @@ class SVN(SCM): @classmethod def value_from_svn_info(cls, path, field_name): svn_info_args = ['svn', 'info', path] - info_output = cls.run_command(svn_info_args) + info_output = run_command(svn_info_args).rstrip() match = re.search("^%s: (?P<value>.+)$" % field_name, info_output, re.MULTILINE) if not match: raise ScriptError(script_args=svn_info_args, message='svn info did not contain a %s.' % field_name) @@ -323,18 +300,15 @@ class SVN(SCM): def svn_version(self): if not self.cached_version: - self.cached_version = self.run_command(['svn', '--version', '--quiet']) + self.cached_version = run_command(['svn', '--version', '--quiet']) return self.cached_version def working_directory_is_clean(self): - return self.run_command(['svn', 'diff']) == "" + return run_command(['svn', 'diff']) == "" def clean_working_directory(self): - self.run_command(['svn', 'revert', '-R', '.']) - - def update_webkit(self): - self.run_command(self.script_path("update-webkit")) + run_command(['svn', 'revert', '-R', '.']) def status_command(self): return ['svn', 'status'] @@ -354,10 +328,10 @@ class SVN(SCM): return "svn" def create_patch(self): - return self.run_command(self.script_path("svn-create-patch"), cwd=self.checkout_root) + return run_command(self.script_path("svn-create-patch"), cwd=self.checkout_root, return_stderr=False) def diff_for_revision(self, revision): - return self.run_command(['svn', 'diff', '-c', str(revision)]) + return run_command(['svn', 'diff', '-c', str(revision)]) def _repository_url(self): return self.value_from_svn_info(self.checkout_root, 'URL') @@ -367,20 +341,20 @@ 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)) - self.run_command(svn_merge_args) + run_command(svn_merge_args) def revert_files(self, file_paths): - self.run_command(['svn', 'revert'] + file_paths) + run_command(['svn', 'revert'] + file_paths) def commit_with_message(self, message): 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 self.run_command(['svn', 'commit', '-m', message], error_handler=commit_error_handler) + return run_command(['svn', 'commit', '-m', message], error_handler=commit_error_handler) def svn_commit_log(self, svn_revision): svn_revision = self.strip_r_from_svn_revision(str(svn_revision)) - return self.run_command(['svn', 'log', '--non-interactive', '--revision', svn_revision]); + return run_command(['svn', 'log', '--non-interactive', '--revision', svn_revision]); def last_svn_commit_log(self): # BASE is the checkout revision, HEAD is the remote repository revision @@ -394,12 +368,12 @@ class Git(SCM): @classmethod def in_working_directory(cls, path): - return cls.run_command(['git', 'rev-parse', '--is-inside-work-tree'], cwd=path, error_handler=ignore_error) == "true" + return run_command(['git', 'rev-parse', '--is-inside-work-tree'], cwd=path, error_handler=Executive.ignore_error).rstrip() == "true" @classmethod def find_checkout_root(cls, path): # "git rev-parse --show-cdup" would be another way to get to the root - (checkout_root, dot_git) = os.path.split(cls.run_command(['git', 'rev-parse', '--git-dir'], cwd=path)) + (checkout_root, dot_git) = os.path.split(run_command(['git', 'rev-parse', '--git-dir'], cwd=path)) # If we were using 2.6 # checkout_root = os.path.relpath(checkout_root, path) if not os.path.isabs(checkout_root): # Sometimes git returns relative paths checkout_root = os.path.join(path, checkout_root) @@ -411,28 +385,23 @@ class Git(SCM): def discard_local_commits(self): - self.run_command(['git', 'reset', '--hard', 'trunk']) + run_command(['git', 'reset', '--hard', 'trunk']) def local_commits(self): - return self.run_command(['git', 'log', '--pretty=oneline', 'HEAD...trunk']).splitlines() + return run_command(['git', 'log', '--pretty=oneline', 'HEAD...trunk']).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 self.run_command(['git', 'diff-index', 'HEAD']) == "" + return run_command(['git', 'diff-index', 'HEAD']) == "" def clean_working_directory(self): # Could run git clean here too, but that wouldn't match working_directory_is_clean - self.run_command(['git', 'reset', '--hard', 'HEAD']) + run_command(['git', 'reset', '--hard', 'HEAD']) # Aborting rebase even though this does not match working_directory_is_clean if self.rebase_in_progress(): - self.run_command(['git', 'rebase', '--abort']) - - def update_webkit(self): - # FIXME: Call update-webkit once https://bugs.webkit.org/show_bug.cgi?id=27162 is fixed. - log("Updating working directory") - self.run_command(['git', 'svn', 'rebase']) + run_command(['git', 'rebase', '--abort']) def status_command(self): return ['git', 'status'] @@ -450,12 +419,12 @@ class Git(SCM): return "git" def create_patch(self): - return self.run_command(['git', 'diff', 'HEAD']) + return run_command(['git', 'diff', '--binary', 'HEAD']) @classmethod def git_commit_from_svn_revision(cls, revision): # git svn find-rev always exits 0, even when the revision is not found. - return cls.run_command(['git', 'svn', 'find-rev', 'r%s' % revision]) + return run_command(['git', 'svn', 'find-rev', 'r%s' % revision]).rstrip() def diff_for_revision(self, revision): git_commit = self.git_commit_from_svn_revision(revision) @@ -469,15 +438,15 @@ class Git(SCM): # I think this will always fail due to ChangeLogs. # FIXME: We need to detec specific failure conditions and handle them. - self.run_command(['git', 'revert', '--no-commit', git_commit], error_handler=ignore_error) + 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): - self.run_command([self.script_path('resolve-ChangeLogs')] + changelog_paths) + run_command([self.script_path('resolve-ChangeLogs')] + changelog_paths) def revert_files(self, file_paths): - self.run_command(['git', 'checkout', 'HEAD'] + file_paths) + run_command(['git', 'checkout', 'HEAD'] + file_paths) def commit_with_message(self, message): self.commit_locally_with_message(message) @@ -485,27 +454,27 @@ class Git(SCM): def svn_commit_log(self, svn_revision): svn_revision = self.strip_r_from_svn_revision(svn_revision) - return self.run_command(['git', 'svn', 'log', '-r', svn_revision]) + return run_command(['git', 'svn', 'log', '-r', svn_revision]) def last_svn_commit_log(self): - return self.run_command(['git', 'svn', 'log', '--limit=1']) + return run_command(['git', 'svn', 'log', '--limit=1']) # Git-specific methods: def create_patch_from_local_commit(self, commit_id): - return self.run_command(['git', 'diff', commit_id + "^.." + commit_id]) + return run_command(['git', 'diff', '--binary', commit_id + "^.." + commit_id]) def create_patch_since_local_commit(self, commit_id): - return self.run_command(['git', 'diff', commit_id]) + return run_command(['git', 'diff', '--binary', commit_id]) def commit_locally_with_message(self, message): - self.run_command(['git', 'commit', '--all', '-F', '-'], input=message) + run_command(['git', 'commit', '--all', '-F', '-'], input=message) def push_local_commits_to_server(self): 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 self.run_command(['git', 'svn', 'dcommit'], error_handler=commit_error_handler) + return run_command(['git', 'svn', 'dcommit'], error_handler=commit_error_handler) # This function supports the following argument formats: # no args : rev-list trunk..HEAD @@ -522,14 +491,14 @@ class Git(SCM): if '...' in commitish: raise ScriptError(message="'...' is not supported (found in '%s'). Did you mean '..'?" % commitish) elif '..' in commitish: - commit_ids += reversed(self.run_command(['git', 'rev-list', commitish]).splitlines()) + commit_ids += reversed(run_command(['git', 'rev-list', commitish]).splitlines()) else: # Turn single commits or branch or tag names into commit ids. - commit_ids += self.run_command(['git', 'rev-parse', '--revs-only', commitish]).splitlines() + commit_ids += run_command(['git', 'rev-parse', '--revs-only', commitish]).splitlines() return commit_ids def commit_message_for_local_commit(self, commit_id): - commit_lines = self.run_command(['git', 'cat-file', 'commit', commit_id]).splitlines() + commit_lines = run_command(['git', 'cat-file', 'commit', commit_id]).splitlines() # Skip the git headers. first_line_after_headers = 0 @@ -540,4 +509,4 @@ class Git(SCM): return CommitMessage(commit_lines[first_line_after_headers:]) def files_changed_summary_for_commit(self, commit_id): - return self.run_command(['git', 'diff-tree', '--shortstat', '--no-commit-id', commit_id]) + return run_command(['git', 'diff-tree', '--shortstat', '--no-commit-id', commit_id]) diff --git a/WebKitTools/Scripts/modules/scm_unittest.py b/WebKitTools/Scripts/modules/scm_unittest.py index 784303f..8e82f3c 100644 --- a/WebKitTools/Scripts/modules/scm_unittest.py +++ b/WebKitTools/Scripts/modules/scm_unittest.py @@ -29,21 +29,22 @@ import base64 import os +import os.path import re import stat import subprocess import tempfile import unittest import urllib -from modules.scm import detect_scm_system, SCM, ScriptError, CheckoutNeedsUpdate, ignore_error, commit_error_handler +from datetime import date +from modules.executive import Executive, run_command, ScriptError +from modules.scm import detect_scm_system, SCM, CheckoutNeedsUpdate, commit_error_handler # 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. -def run(args, cwd=None): - return SCM.run_command(args, cwd=cwd) - +# FIXME: This should be unified into one of the executive.py commands! def run_silent(args, cwd=None): process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) process.communicate() # ignore output @@ -72,26 +73,26 @@ class SVNTestRepository: test_file.write("test1") test_file.flush() - run(['svn', 'add', 'test_file']) - run(['svn', 'commit', '--quiet', '--message', 'initial commit']) + run_command(['svn', 'add', 'test_file']) + run_command(['svn', 'commit', '--quiet', '--message', 'initial commit']) test_file.write("test2") test_file.flush() - run(['svn', 'commit', '--quiet', '--message', 'second commit']) + run_command(['svn', 'commit', '--quiet', '--message', 'second commit']) test_file.write("test3\n") test_file.flush() - run(['svn', 'commit', '--quiet', '--message', 'third commit']) + run_command(['svn', 'commit', '--quiet', '--message', 'third commit']) test_file.write("test4\n") test_file.close() - run(['svn', 'commit', '--quiet', '--message', 'fourth commit']) + run_command(['svn', 'commit', '--quiet', '--message', 'fourth commit']) # svn does not seem to update after commit as I would expect. - run(['svn', 'update']) + run_command(['svn', 'update']) @classmethod def setup(cls, test_object): @@ -100,18 +101,18 @@ class SVNTestRepository: test_object.svn_repo_url = "file://%s" % test_object.svn_repo_path # Not sure this will work on windows # git svn complains if we don't pass --pre-1.5-compatible, not sure why: # Expected FS format '2'; found format '3' at /usr/local/libexec/git-core//git-svn line 1477 - run(['svnadmin', 'create', '--pre-1.5-compatible', test_object.svn_repo_path]) + run_command(['svnadmin', 'create', '--pre-1.5-compatible', test_object.svn_repo_path]) # Create a test svn checkout test_object.svn_checkout_path = tempfile.mkdtemp(suffix="svn_test_checkout") - run(['svn', 'checkout', '--quiet', test_object.svn_repo_url, test_object.svn_checkout_path]) + run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url, test_object.svn_checkout_path]) cls._setup_test_commits(test_object) @classmethod def tear_down(cls, test_object): - run(['rm', '-rf', test_object.svn_repo_path]) - run(['rm', '-rf', test_object.svn_checkout_path]) + run_command(['rm', '-rf', test_object.svn_repo_path]) + run_command(['rm', '-rf', test_object.svn_checkout_path]) # For testing the SCM baseclass directly. class SCMClassTests(unittest.TestCase): @@ -122,21 +123,21 @@ class SCMClassTests(unittest.TestCase): self.dev_null.close() def test_run_command_with_pipe(self): - input_process = subprocess.Popen(['/bin/echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) - self.assertEqual(SCM.run_command(['/usr/bin/grep', 'bar'], input=input_process.stdout), "bar") + input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) + self.assertEqual(run_command(['grep', 'bar'], input=input_process.stdout), "bar\n") # Test the non-pipe case too: - self.assertEqual(SCM.run_command(['/usr/bin/grep', 'bar'], input="foo\nbar"), "bar") + self.assertEqual(run_command(['grep', 'bar'], input="foo\nbar"), "bar\n") command_returns_non_zero = ['/bin/sh', '--invalid-option'] # Test when the input pipe process fails. input_process = subprocess.Popen(command_returns_non_zero, stdout=subprocess.PIPE, stderr=self.dev_null) self.assertTrue(input_process.poll() != 0) - self.assertRaises(ScriptError, SCM.run_command, ['/usr/bin/grep', 'bar'], input=input_process.stdout) + self.assertRaises(ScriptError, run_command, ['grep', 'bar'], input=input_process.stdout) # Test when the run_command process fails. - input_process = subprocess.Popen(['/bin/echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) # grep shows usage and calls exit(2) when called w/o arguments. - self.assertRaises(ScriptError, SCM.run_command, command_returns_non_zero, input=input_process.stdout) + input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) # grep shows usage and calls exit(2) when called w/o arguments. + self.assertRaises(ScriptError, run_command, command_returns_non_zero, input=input_process.stdout) def test_error_handlers(self): git_failure_message="Merge conflict during commit: Your file or directory 'WebCore/ChangeLog' is probably out-of-date: resource out of date; try updating at /usr/local/libexec/git-core//git-svn line 469" @@ -145,12 +146,13 @@ svn: File or directory 'ChangeLog' is out of date; try updating svn: resource out of date; try updating """ command_does_not_exist = ['does_not_exist', 'invalid_option'] - self.assertRaises(OSError, SCM.run_command, command_does_not_exist) - self.assertRaises(OSError, SCM.run_command, command_does_not_exist, error_handler=ignore_error) + self.assertRaises(OSError, run_command, command_does_not_exist) + self.assertRaises(OSError, run_command, command_does_not_exist, error_handler=Executive.ignore_error) command_returns_non_zero = ['/bin/sh', '--invalid-option'] - self.assertRaises(ScriptError, SCM.run_command, command_returns_non_zero) - self.assertTrue(SCM.run_command(command_returns_non_zero, error_handler=ignore_error)) + self.assertRaises(ScriptError, run_command, command_returns_non_zero) + # Check if returns error text: + self.assertTrue(run_command(command_returns_non_zero, error_handler=Executive.ignore_error)) self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=git_failure_message)) self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=svn_failure_message)) @@ -201,9 +203,185 @@ class SCMTest(unittest.TestCase): self.assertTrue(re.search('test2', r3_patch)) self.assertTrue(re.search('test2', self.scm.diff_for_revision(2))) + def _shared_test_svn_apply_git_patch(self): + self._setup_webkittools_scripts_symlink(self.scm) + git_binary_addition = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif +new file mode 100644 +index 0000000000000000000000000000000000000000..64a9532e7794fcd791f6f12157406d90 +60151690 +GIT binary patch +literal 512 +zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c? +zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap +zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ +zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A +zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&) +zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b +zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB +z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X +z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4 +ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H + +literal 0 +HcmV?d00001 + +""" + self.scm.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)) + + git_binary_modification = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif +index 64a9532e7794fcd791f6f12157406d9060151690..323fae03f4606ea9991df8befbb2fca7 +GIT binary patch +literal 7 +OcmYex&reD$;sO8*F9L)B + +literal 512 +zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c? +zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap +zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ +zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A +zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&) +zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b +zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB +z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X +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)) + 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)) + + git_binary_deletion = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif +deleted file mode 100644 +index 323fae0..0000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 7 +OcmYex&reD$;sO8*F9L)B + +""" + self.scm.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)) + class SVNTest(SCMTest): + @staticmethod + def _set_date_and_reviewer(changelog_entry): + # Joe Cool matches the reviewer set in SCMTest._create_patch + changelog_entry = changelog_entry.replace('REVIEWER_HERE', 'Joe Cool') + # svn-apply will update ChangeLog entries with today's date. + return changelog_entry.replace('DATE_HERE', date.today().isoformat()) + + def test_svn_apply(self): + first_entry = """2009-10-26 Eric Seidel <eric@webkit.org> + + Reviewed by Foo Bar. + + Most awesome change ever. + + * scm_unittest.py: +""" + intermediate_entry = """2009-10-27 Eric Seidel <eric@webkit.org> + + Reviewed by Baz Bar. + + A more awesomer change yet! + + * scm_unittest.py: +""" + one_line_overlap_patch = """Index: ChangeLog +=================================================================== +--- ChangeLog (revision 5) ++++ ChangeLog (working copy) +@@ -1,5 +1,13 @@ + 2009-10-26 Eric Seidel <eric@webkit.org> + ++ Reviewed by NOBODY (OOPS!). ++ ++ Second most awsome 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. + + * scm_unittest.py: +""" + two_line_overlap_patch = """Index: ChangeLog +=================================================================== +--- ChangeLog (revision 5) ++++ ChangeLog (working copy) +@@ -2,6 +2,14 @@ + + Reviewed by Foo Bar. + ++ Second most awsome change ever. ++ ++ * scm_unittest.py: ++ ++2009-10-26 Eric Seidel <eric@webkit.org> ++ ++ 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. + + * scm_unittest.py: +""" + write_into_file_at_path('ChangeLog', first_entry) + run_command(['svn', 'add', 'ChangeLog']) + run_command(['svn', 'commit', '--quiet', '--message', 'ChangeLog commit']) + + # Patch files were created against just 'first_entry'. + # Add a second commit to make svn-apply have to apply the patches with fuzz. + changelog_contents = "%s\n%s" % (intermediate_entry, first_entry) + write_into_file_at_path('ChangeLog', changelog_contents) + 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)) + 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)) + 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) + def setUp(self): SVNTestRepository.setup(self) os.chdir(self.svn_checkout_path) @@ -217,13 +395,13 @@ class SVNTest(SCMTest): 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(['svn', 'add', 'test_dir']) + run_command(['svn', 'add', 'test_dir']) # create_patch depends on 'svn-create-patch', so make a dummy version. scripts_path = os.path.join(self.svn_checkout_path, 'WebKitTools', 'Scripts') os.makedirs(scripts_path) create_patch_path = os.path.join(scripts_path, 'svn-create-patch') - write_into_file_at_path(create_patch_path, '#!/bin/sh\necho $PWD') + write_into_file_at_path(create_patch_path, '#!/bin/sh\necho $PWD') # We could pass -n to prevent the \n, but not all echo accept -n. os.chmod(create_patch_path, stat.S_IXUSR | stat.S_IRUSR) # Change into our test directory and run the create_patch command. @@ -232,7 +410,7 @@ class SVNTest(SCMTest): self.assertEqual(scm.checkout_root, self.svn_checkout_path) # Sanity check that detection worked right. patch_contents = scm.create_patch() # Our fake 'svn-create-patch' returns $PWD instead of a patch, check that it was executed from the root of the repo. - self.assertEqual(os.path.realpath(scm.checkout_root), patch_contents) + self.assertEqual("%s\n" % os.path.realpath(scm.checkout_root), patch_contents) # Add a \n because echo adds a \n. def test_detection(self): scm = detect_scm_system(self.svn_checkout_path) @@ -262,13 +440,13 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== def test_apply_svn_patch(self): scm = detect_scm_system(self.svn_checkout_path) - patch = self._create_patch(run(['svn', 'diff', '-r4:3'])) + patch = self._create_patch(run_command(['svn', 'diff', '-r4:3'])) self._setup_webkittools_scripts_symlink(scm) scm.apply_patch(patch) def test_apply_svn_patch_force(self): scm = detect_scm_system(self.svn_checkout_path) - patch = self._create_patch(run(['svn', 'diff', '-r2:4'])) + 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) @@ -286,6 +464,8 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== def test_diff_for_revision(self): self._shared_test_diff_for_revision() + def test_svn_apply_git_patch(self): + self._shared_test_svn_apply_git_patch() class GitTest(SCMTest): @@ -295,7 +475,7 @@ class GitTest(SCMTest): run_silent(['git', 'svn', '--quiet', 'clone', self.svn_repo_url, self.git_checkout_path]) def _tear_down_git_clone_of_svn_repository(self): - run(['rm', '-rf', self.git_checkout_path]) + run_command(['rm', '-rf', self.git_checkout_path]) def setUp(self): SVNTestRepository.setup(self) @@ -315,11 +495,11 @@ class GitTest(SCMTest): 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") - run(['svn', 'commit', '--message', 'commit to conflict with git commit'], cwd=self.svn_checkout_path) + run_command(['svn', 'commit', '--message', 'commit to conflict with git commit'], cwd=self.svn_checkout_path) git_test_file = os.path.join(self.git_checkout_path, 'test_file') write_into_file_at_path(git_test_file, "git_checkout") - run(['git', 'commit', '-a', '-m', 'commit to be thrown away by rebase abort']) + run_command(['git', 'commit', '-a', '-m', 'commit to be thrown away by rebase abort']) # --quiet doesn't make git svn silent, so use run_silent to redirect output self.assertRaises(ScriptError, run_silent, ['git', 'svn', '--quiet', 'rebase']) # Will fail due to a conflict leaving us mid-rebase. @@ -351,19 +531,19 @@ class GitTest(SCMTest): actual_commits = scm.commit_ids_from_commitish_arguments([commit_range]) expected_commits = [] - expected_commits += reversed(run(['git', 'rev-list', commit_range]).splitlines()) + expected_commits += reversed(run_command(['git', 'rev-list', commit_range]).splitlines()) self.assertEqual(actual_commits, expected_commits) def test_apply_git_patch(self): scm = detect_scm_system(self.git_checkout_path) - patch = self._create_patch(run(['git', 'diff', 'HEAD..HEAD^'])) + patch = self._create_patch(run_command(['git', 'diff', 'HEAD..HEAD^'])) self._setup_webkittools_scripts_symlink(scm) scm.apply_patch(patch) def test_apply_git_patch_force(self): scm = detect_scm_system(self.git_checkout_path) - patch = self._create_patch(run(['git', 'diff', 'HEAD~2..HEAD'])) + 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) @@ -376,5 +556,39 @@ class GitTest(SCMTest): def test_diff_for_revision(self): self._shared_test_diff_for_revision() + def test_svn_apply_git_patch(self): + self._shared_test_svn_apply_git_patch() + + def test_create_binary_patch(self): + # Create a git binary patch and check the contents. + scm = detect_scm_system(self.git_checkout_path) + test_file_name = 'binary_file' + test_file_path = os.path.join(self.git_checkout_path, test_file_name) + file_contents = ''.join(map(chr, range(256))) + write_into_file_at_path(test_file_path, file_contents) + run_command(['git', 'add', test_file_name]) + patch = scm.create_patch() + self.assertTrue(re.search(r'\nliteral 0\n', patch)) + self.assertTrue(re.search(r'\nliteral 256\n', patch)) + + # 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.assertEqual(file_contents, read_from_path(test_file_path)) + + # Check if we can create a patch from a local commit. + write_into_file_at_path(test_file_path, file_contents) + run_command(['git', 'add', test_file_name]) + run_command(['git', 'commit', '-m', 'binary diff']) + patch_from_local_commit = scm.create_patch_from_local_commit('HEAD') + self.assertTrue(re.search(r'\nliteral 0\n', patch_from_local_commit)) + self.assertTrue(re.search(r'\nliteral 256\n', patch_from_local_commit)) + patch_since_local_commit = scm.create_patch_since_local_commit('HEAD^1') + self.assertTrue(re.search(r'\nliteral 0\n', patch_since_local_commit)) + self.assertTrue(re.search(r'\nliteral 256\n', patch_since_local_commit)) + self.assertEqual(patch_from_local_commit, patch_since_local_commit) + + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/modules/statusbot.py b/WebKitTools/Scripts/modules/statusbot.py index 9c9ba04..350aebf 100644 --- a/WebKitTools/Scripts/modules/statusbot.py +++ b/WebKitTools/Scripts/modules/statusbot.py @@ -46,21 +46,44 @@ http://wwwsearch.sourceforge.net/mechanize/ """ exit(1) +import urllib2 + + class StatusBot: default_host = "webkit-commit-queue.appspot.com" def __init__(self, host=default_host): + self.set_host(host) + self.browser = Browser() + + def set_host(self, host): self.statusbot_host = host self.statusbot_server_url = "http://%s" % self.statusbot_host - self.update_status_url = "%s/update_status" % self.statusbot_server_url - self.browser = Browser() - def update_status(self, status, bug_id=None, patch_id=None): - self.browser.open(self.update_status_url) + def update_status(self, queue_name, status, patch=None, results_file=None): + # During unit testing, statusbot_host is None + if not self.statusbot_host: + return + + update_status_url = "%s/update-status" % self.statusbot_server_url + self.browser.open(update_status_url) self.browser.select_form(name="update_status") - if bug_id: - self.browser['bug_id'] = str(bug_id) - if patch_id: - self.browser['patch_id'] = str(patch_id) + self.browser['queue_name'] = queue_name + if patch: + if patch.get('bug_id'): + self.browser['bug_id'] = str(patch['bug_id']) + if patch.get('id'): + self.browser['patch_id'] = str(patch['id']) self.browser['status'] = status + if results_file: + self.browser.add_file(results_file, "text/plain", "results.txt", 'results_file') self.browser.submit() + + def patch_status(self, queue_name, patch_id): + update_status_url = "%s/patch-status/%s/%s" % (self.statusbot_server_url, queue_name, patch_id) + try: + return urllib2.urlopen(update_status_url).read() + except urllib2.HTTPError, e: + if e.code == 404: + return None + raise e diff --git a/WebKitTools/Scripts/modules/stepsequence.py b/WebKitTools/Scripts/modules/stepsequence.py new file mode 100644 index 0000000..6f085c9 --- /dev/null +++ b/WebKitTools/Scripts/modules/stepsequence.py @@ -0,0 +1,68 @@ +# 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 modules.buildsteps import CommandOptions +from modules.executive import ScriptError +from modules.logging import log +from modules.scm import CheckoutNeedsUpdate +from modules.workqueue import WorkQueue + + +class StepSequence(object): + def __init__(self, steps): + self._steps = steps + + def options(self): + collected_options = [ + CommandOptions.parent_command, + CommandOptions.quiet, + ] + for step in self._steps: + collected_options = collected_options + step.options() + # Remove duplicates. + collected_options = sorted(set(collected_options)) + return collected_options + + def _run(self, tool, options, patch): + for step in self._steps: + step(tool, options, patch).run() + + def run_and_handle_errors(self, tool, options, patch=None): + try: + self._run(tool, options, patch) + 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.") + WorkQueue.exit_after_handled_error(e) + except ScriptError, e: + if not options.quiet: + log(e.message_with_output()) + if options.parent_command: + command = tool.command_by_name(options.parent_command) + command.handle_script_error(tool, patch, e) + WorkQueue.exit_after_handled_error(e) diff --git a/WebKitTools/Scripts/modules/webkitport.py b/WebKitTools/Scripts/modules/webkitport.py new file mode 100644 index 0000000..849ac4b --- /dev/null +++ b/WebKitTools/Scripts/modules/webkitport.py @@ -0,0 +1,118 @@ +# 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 understanding the various ports + +import os + +from optparse import make_option + +class WebKitPort(): + # We might need to pass scm into this function for scm.checkout_root + @classmethod + def script_path(cls, script_name): + return os.path.join("WebKitTools", "Scripts", script_name) + + @staticmethod + def port(port_name): + if port_name == "mac": + return MacPort + if port_name == "qt": + return QtPort + if port_name == "chromium": + return ChromiumPort + # FIXME: We should default to WinPort on Windows. + return MacPort + + @classmethod + def name(cls): + raise NotImplementedError, "subclasses must implement" + + @classmethod + def flag(cls): + raise NotImplementedError, "subclasses must implement" + + @classmethod + def update_webkit_command(cls): + return [cls.script_path("update-webkit")] + + @classmethod + def build_webkit_command(cls): + return [cls.script_path("build-webkit")] + + @classmethod + def run_webkit_tests_command(cls): + return [cls.script_path("run-webkit-tests")] + + +class MacPort(WebKitPort): + @classmethod + def name(cls): + return "Mac" + + @classmethod + def flag(cls): + return "--port=mac" + + +class QtPort(WebKitPort): + @classmethod + def name(cls): + return "Qt" + + @classmethod + def flag(cls): + return "--port=qt" + + @classmethod + def build_webkit_command(cls): + command = WebKitPort.build_webkit_command() + command.append("--qt") + return command + + +class ChromiumPort(WebKitPort): + @classmethod + def name(cls): + return "Chromium" + + @classmethod + def flag(cls): + return "--port=chromium" + + @classmethod + def update_webkit_command(cls): + command = WebKitPort.update_webkit_command() + command.append("--chromium") + return command + + @classmethod + def build_webkit_command(cls): + command = WebKitPort.build_webkit_command() + command.append("--chromium") + return command diff --git a/WebKitTools/Scripts/modules/webkitport_unittest.py b/WebKitTools/Scripts/modules/webkitport_unittest.py new file mode 100644 index 0000000..c713e83 --- /dev/null +++ b/WebKitTools/Scripts/modules/webkitport_unittest.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# 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 modules.webkitport import WebKitPort, MacPort, QtPort, ChromiumPort + +class WebKitPortTest(unittest.TestCase): + def test_mac_port(self): + self.assertEquals(MacPort.name(), "Mac") + self.assertEquals(MacPort.flag(), "--port=mac") + self.assertEquals(MacPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests")]) + self.assertEquals(MacPort.build_webkit_command(), [WebKitPort.script_path("build-webkit")]) + + 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"]) + + def test_chromium_port(self): + self.assertEquals(ChromiumPort.name(), "Chromium") + self.assertEquals(ChromiumPort.flag(), "--port=chromium") + self.assertEquals(ChromiumPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests")]) + self.assertEquals(ChromiumPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--chromium"]) + self.assertEquals(ChromiumPort.update_webkit_command(), [WebKitPort.script_path("update-webkit"), "--chromium"]) + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/modules/workqueue.py b/WebKitTools/Scripts/modules/workqueue.py new file mode 100644 index 0000000..f8cbba8 --- /dev/null +++ b/WebKitTools/Scripts/modules/workqueue.py @@ -0,0 +1,159 @@ +#!/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. + +import os +import time +import traceback + +from datetime import datetime, timedelta + +from modules.executive import ScriptError +from modules.logging import log, OutputTee +from modules.statusbot import StatusBot + +class WorkQueueDelegate: + def queue_name(self): + raise NotImplementedError, "subclasses must implement" + + def queue_log_path(self): + raise NotImplementedError, "subclasses must implement" + + def work_logs_directory(self): + raise NotImplementedError, "subclasses must implement" + + def status_host(self): + raise NotImplementedError, "subclasses must implement" + + def begin_work_queue(self): + raise NotImplementedError, "subclasses must implement" + + def should_continue_work_queue(self): + raise NotImplementedError, "subclasses must implement" + + def next_work_item(self): + raise NotImplementedError, "subclasses must implement" + + def should_proceed_with_work_item(self, work_item): + # returns (safe_to_proceed, waiting_message, patch) + raise NotImplementedError, "subclasses must implement" + + def process_work_item(self, work_item): + raise NotImplementedError, "subclasses must implement" + + def handle_unexpected_error(self, work_item, message): + raise NotImplementedError, "subclasses must implement" + + +class WorkQueue: + def __init__(self, name, delegate): + self._name = name + self._delegate = delegate + self._output_tee = OutputTee() + + log_date_format = "%Y-%m-%d %H:%M:%S" + sleep_duration_text = "5 mins" + seconds_to_sleep = 300 + handled_error_code = 2 + + # Child processes exit with a special code to the parent queue process can detect the error was handled. + @classmethod + def exit_after_handled_error(cls, error): + log(error) + exit(cls.handled_error_code) + + def run(self): + self._begin_logging() + self.status_bot = StatusBot(host=self._delegate.status_host()) + + self._delegate.begin_work_queue() + while (self._delegate.should_continue_work_queue()): + self._ensure_work_log_closed() + try: + work_item = self._delegate.next_work_item() + if not work_item: + self._update_status_and_sleep("Empty queue.") + continue + (safe_to_proceed, waiting_message, patch) = self._delegate.should_proceed_with_work_item(work_item) + if not safe_to_proceed: + self._update_status_and_sleep(waiting_message) + continue + self.status_bot.update_status(self._name, waiting_message, patch) + except KeyboardInterrupt, e: + log("\nUser terminated queue.") + return 1 + except Exception, e: + traceback.print_exc() + # Don't try tell the status bot, in case telling it causes an exception. + self._sleep("Exception while preparing queue: %s." % e) + continue + + # FIXME: Work logs should not depend on bug_id specificaly. + self._open_work_log(patch["bug_id"]) + try: + self._delegate.process_work_item(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. + if e.exit_code == self.handled_error_code: + continue + message = "Unexpected failure when landing patch! Please file a bug against bugzilla-tool.\n%s" % e.message_with_output() + self._delegate.handle_unexpected_error(work_item, message) + # Never reached. + self._ensure_work_log_closed() + + def _begin_logging(self): + self._queue_log = self._output_tee.add_log(self._delegate.queue_log_path()) + self._work_log = None + + def _open_work_log(self, bug_id): + work_log_path = os.path.join(self._delegate.work_logs_directory(), "%s.log" % bug_id) + self._work_log = self._output_tee.add_log(work_log_path) + + def _ensure_work_log_closed(self): + # If we still have a bug log open, close it. + if self._work_log: + 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) + + @classmethod + def _sleep(cls, message): + log(cls._sleep_message(message)) + time.sleep(cls.seconds_to_sleep) + + def _update_status_and_sleep(self, message): + status_message = self._sleep_message(message) + self.status_bot.update_status(self._name, status_message) + log(status_message) + time.sleep(self.seconds_to_sleep) diff --git a/WebKitTools/Scripts/modules/workqueue_unittest.py b/WebKitTools/Scripts/modules/workqueue_unittest.py new file mode 100644 index 0000000..ed77b5f --- /dev/null +++ b/WebKitTools/Scripts/modules/workqueue_unittest.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# 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 +import shutil +import tempfile +import unittest + +from modules.executive import ScriptError +from modules.workqueue import WorkQueue, WorkQueueDelegate + +class LoggingDelegate(WorkQueueDelegate): + def __init__(self, test): + self._test = test + self._callbacks = [] + self._run_before = False + + expected_callbacks = [ + 'queue_log_path', + 'status_host', + 'begin_work_queue', + 'should_continue_work_queue', + 'next_work_item', + 'should_proceed_with_work_item', + 'work_logs_directory', + 'process_work_item', + 'should_continue_work_queue' + ] + + def record(self, method_name): + self._callbacks.append(method_name) + + def queue_log_path(self): + self.record("queue_log_path") + return os.path.join(self._test.temp_dir, "queue_log_path") + + def work_logs_directory(self): + self.record("work_logs_directory") + return os.path.join(self._test.temp_dir, "work_log_path") + + def status_host(self): + self.record("status_host") + return None + + def begin_work_queue(self): + self.record("begin_work_queue") + + def should_continue_work_queue(self): + self.record("should_continue_work_queue") + if not self._run_before: + self._run_before = True + return True + return False + + def next_work_item(self): + self.record("next_work_item") + return "work_item" + + def should_proceed_with_work_item(self, work_item): + self.record("should_proceed_with_work_item") + self._test.assertEquals(work_item, "work_item") + fake_patch = { 'bug_id' : 42 } + return (True, "waiting_message", fake_patch) + + def process_work_item(self, work_item): + self.record("process_work_item") + self._test.assertEquals(work_item, "work_item") + + def handle_unexpected_error(self, work_item, message): + self.record("handle_unexpected_error") + self._test.assertEquals(work_item, "work_item") + + +class ThrowErrorDelegate(LoggingDelegate): + def __init__(self, test, error_code): + LoggingDelegate.__init__(self, test) + self.error_code = error_code + + def process_work_item(self, work_item): + self.record("process_work_item") + raise ScriptError(exit_code=self.error_code) + + +class NotSafeToProceedDelegate(LoggingDelegate): + def should_proceed_with_work_item(self, work_item): + self.record("should_proceed_with_work_item") + self._test.assertEquals(work_item, "work_item") + fake_patch = { 'bug_id' : 42 } + return (False, "waiting_message", fake_patch) + + +class FastWorkQueue(WorkQueue): + def __init__(self, delegate): + WorkQueue.__init__(self, "fast-queue", delegate) + + # No sleep for the wicked. + seconds_to_sleep = 0 + + def _update_status_and_sleep(self, message): + pass + + +class WorkQueueTest(unittest.TestCase): + def test_trivial(self): + delegate = LoggingDelegate(self) + work_queue = WorkQueue("trivial-queue", delegate) + work_queue.run() + self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks) + self.assertTrue(os.path.exists(delegate.queue_log_path())) + self.assertTrue(os.path.exists(os.path.join(delegate.work_logs_directory(), "42.log"))) + + def test_unexpected_error(self): + delegate = ThrowErrorDelegate(self, 3) + work_queue = WorkQueue("error-queue", delegate) + work_queue.run() + expected_callbacks = LoggingDelegate.expected_callbacks[:] + work_item_index = expected_callbacks.index('process_work_item') + # The unexpected error should be handled right after process_work_item starts + # but before any other callback. Otherwise callbacks should be normal. + expected_callbacks.insert(work_item_index + 1, 'handle_unexpected_error') + self.assertEquals(delegate._callbacks, expected_callbacks) + + def test_handled_error(self): + delegate = ThrowErrorDelegate(self, WorkQueue.handled_error_code) + work_queue = WorkQueue("handled-error-queue", delegate) + work_queue.run() + self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks) + + def test_not_safe_to_proceed(self): + delegate = NotSafeToProceedDelegate(self) + work_queue = FastWorkQueue(delegate) + work_queue.run() + expected_callbacks = LoggingDelegate.expected_callbacks[:] + next_work_item_index = expected_callbacks.index('next_work_item') + # We slice out the common part of the expected callbacks. + # We add 2 here to include should_proceed_with_work_item, which is + # a pain to search for directly because it occurs twice. + expected_callbacks = expected_callbacks[:next_work_item_index + 2] + expected_callbacks.append('should_continue_work_queue') + self.assertEquals(delegate._callbacks, expected_callbacks) + + def setUp(self): + self.temp_dir = tempfile.mkdtemp(suffix="work_queue_test_logs") + + def tearDown(self): + shutil.rmtree(self.temp_dir) + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/prepare-ChangeLog b/WebKitTools/Scripts/prepare-ChangeLog index dd864df..4c59af9 100755 --- a/WebKitTools/Scripts/prepare-ChangeLog +++ b/WebKitTools/Scripts/prepare-ChangeLog @@ -65,8 +65,8 @@ use POSIX qw(strftime); use VCSUtils; sub changeLogDate($); -sub changeLogEmailAddress($); -sub changeLogName($); +sub changeLogEmailAddressFromArgs($); +sub changeLogNameFromArgs($); sub firstDirectoryOrCwd(); sub diffFromToString(); sub diffCommand(@); @@ -77,7 +77,6 @@ sub findOriginalFileFromSvn($); sub determinePropertyChanges($$$); sub pluralizeAndList($$@); sub generateFileList(\@\@\%); -sub gitConfig($); sub isUnmodifiedStatus($); sub isModifiedStatus($); sub isAddedStatus($); @@ -246,8 +245,8 @@ if (%changed_line_ranges) { # Get some parameters for the ChangeLog we are about to write. my $date = changeLogDate($changeLogTimeZone); -$name = changeLogName($name); -$emailAddress = changeLogEmailAddress($emailAddress); +$name = changeLogNameFromArgs($name); +$emailAddress = changeLogEmailAddressFromArgs($emailAddress); print STDERR " Change author: $name <$emailAddress>.\n"; @@ -443,62 +442,22 @@ sub changeLogDate($) return $date; } -sub changeLogNameError($) -{ - my ($message) = @_; - print STDERR "$message\nEither:\n"; - print STDERR " set CHANGE_LOG_NAME in your environment\n"; - print STDERR " OR pass --name= on the command line\n"; - print STDERR " OR set REAL_NAME in your environment"; - print STDERR " OR git users can set 'git config user.name'\n"; - exit(1); -} - -sub changeLogName($) +sub changeLogNameFromArgs($) { my ($nameFromArgs) = @_; - # Silently allow --git-commit to win, we could warn if $emailAddressFromArgs is defined. + # Silently allow --git-commit to win, we could warn if $nameFromArgs is defined. return `$GIT log --max-count=1 --pretty=\"format:%an\" \"$gitCommit\"` if $gitCommit; - my $name = $nameFromArgs - || $ENV{CHANGE_LOG_NAME} - || $ENV{REAL_NAME} - || gitConfig("user.name") - || (split /\s*,\s*/, (getpwuid $<)[6])[0]; - - changeLogNameError("Failed to determine ChangeLog name.") unless $name; - # getpwuid seems to always succeed on windows, returning the username instead of the full name. This check will catch that case. - changeLogNameError("'$name' does not contain a space! ChangeLogs should contain your full name.") unless ($name =~ /\w \w/); - - return $name; -} - -sub changeLogEmailAddressError($) -{ - my ($message) = @_; - print STDERR "$message\nEither:\n"; - print STDERR " set CHANGE_LOG_EMAIL_ADDRESS in your environment\n"; - print STDERR " OR pass --email= on the command line\n"; - print STDERR " OR set EMAIL_ADDRESS in your environment\n"; - print STDERR " OR git users can set 'git config user.email'\n"; - exit(1); + return $nameFromArgs || changeLogName(); } -sub changeLogEmailAddress($) +sub changeLogEmailAddressFromArgs($) { my ($emailAddressFromArgs) = @_; # Silently allow --git-commit to win, we could warn if $emailAddressFromArgs is defined. return `$GIT log --max-count=1 --pretty=\"format:%ae\" \"$gitCommit\"` if $gitCommit; - my $emailAddress = $emailAddressFromArgs - || $ENV{CHANGE_LOG_EMAIL_ADDRESS} - || $ENV{EMAIL_ADDRESS} - || gitConfig("user.email"); - - changeLogEmailAddressError("Failed to determine email address for ChangeLog.") unless $emailAddress; - changeLogEmailAddressError("Email address '$emailAddress' does not contain '\@' and is likely invalid.") unless ($emailAddress =~ /\@/); - - return $emailAddress; + return $emailAddressFromArgs || changeLogEmailAddress(); } sub get_function_line_ranges($$) @@ -1477,20 +1436,6 @@ sub generateFileList(\@\@\%) close STAT; } -sub gitConfig($) -{ - return unless $isGit; - - my ($config) = @_; - - my $result = `$GIT config $config`; - if (($? >> 8) != 0) { - $result = `$GIT repo-config $config`; - } - chomp $result; - return $result; -} - sub isUnmodifiedStatus($) { my ($status) = @_; diff --git a/WebKitTools/Scripts/run-webkit-tests b/WebKitTools/Scripts/run-webkit-tests index 6056035..6dd8339 100755 --- a/WebKitTools/Scripts/run-webkit-tests +++ b/WebKitTools/Scripts/run-webkit-tests @@ -76,6 +76,7 @@ sub buildPlatformTestHierarchy(@); sub closeCygpaths(); sub closeDumpTool(); sub closeHTTPD(); +sub closeWebSocketServer(); sub countAndPrintLeaks($$$); sub countFinishedTest($$$$); sub deleteExpectedAndActualResults($); @@ -86,15 +87,18 @@ sub fileNameWithNumber($$); sub htmlForResultsSection(\@$&); sub isTextOnlyTest($); sub launchWithCurrentEnv(@); +sub resolveAndMakeTestResultsDirectory(); sub numericcmp($$); sub openDiffTool(); sub openDumpTool(); sub openHTTPDIfNeeded(); sub parseLeaksandPrintUniqueLeaks(); +sub openWebSocketServerIfNeeded(); sub pathcmp($$); sub printFailureMessageForTest($$); sub processIgnoreTests($$); sub readFromDumpToolWithTimer(**); +sub readSkippedFiles($); sub recordActualResultsAndDiff($$); sub sampleDumpTool(); sub setFileHandleNonBlocking(*$); @@ -117,6 +121,9 @@ 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; @@ -185,6 +192,12 @@ if (isAppleMacWebKit()) { } } 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()) { @@ -292,8 +305,6 @@ if (!$getOptionsResult || $showHelp) { my $ignoreSkipped = $treatSkipped eq "ignore"; my $skippedOnly = $treatSkipped eq "only"; -!$skippedOnly || @ARGV == 0 or die "--skipped=only cannot be used when tests are specified on the command line."; - my $configuration = configuration(); $verbose = 1 if $testsPerDumpTool == 1; @@ -347,12 +358,12 @@ if (!defined($root)) { } my $dumpToolName = "DumpRenderTree"; -$dumpToolName .= "_debug" if isCygwin() && $configuration ne "Release"; +$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() && $configuration ne "Release"; +$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(); @@ -412,6 +423,7 @@ if (checkWebCoreSVGSupport(0)) { if (!$testHTTP) { $ignoredDirectories{'http'} = 1; + $ignoredDirectories{'websocket'} = 1; } if (!$testMedia) { @@ -449,7 +461,17 @@ if (!checkWebCoreWCSSSupport(0)) { } processIgnoreTests($ignoreTests, "ignore-tests") if $ignoreTests; -readSkippedFiles() unless $ignoreSkipped; +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(); @@ -485,6 +507,11 @@ 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"; @@ -577,15 +604,7 @@ for my $test (@tests) { } } - if ($test !~ /^http\//) { - my $testPath = "$testDirectory/$test"; - if (isCygwin()) { - $testPath = toWindowsPath($testPath); - } else { - $testPath = canonpath($testPath); - } - print OUT "$testPath$suffixExpectedHash\n"; - } else { + if ($test =~ /^http\//) { openHTTPDIfNeeded(); if ($test !~ /^http\/tests\/local\// && $test !~ /^http\/tests\/ssl\// && $test !~ /^http\/tests\/wml\// && $test !~ /^http\/tests\/media\//) { my $path = canonpath($test); @@ -604,6 +623,41 @@ for my $test (@tests) { } 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"; } # DumpRenderTree is expected to dump two "blocks" to stdout for each test. @@ -892,6 +946,7 @@ printf "\n%0.2fs total testing time\n", (time - $overallStartTime) . ""; !$isDumpToolOpen || die "Failed to close $dumpToolName.\n"; closeHTTPD(); +closeWebSocketServer(); # Because multiple instances of this script are running concurrently we cannot # safely delete this symlink. @@ -966,7 +1021,10 @@ close HTML; my @configurationArgs = argumentsForConfiguration(); -if (isQt() || isGtk()) { +if (isGtk()) { + system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari; +} elsif (isQt()) { + unshift @configurationArgs, qw(-graphicssystem raster -style windows); system "WebKitTools/Scripts/run-launcher", @configurationArgs, "file://".$testResults if $launchSafari; } elsif (isCygwin()) { system "cygstart", $testResults if $launchSafari; @@ -1166,6 +1224,13 @@ sub launchWithCurrentEnv(@) return @args; } +sub resolveAndMakeTestResultsDirectory() +{ + my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory); + mkpath $absTestResultsDirectory; + return $absTestResultsDirectory; +} + sub openDiffTool() { return if $isDiffToolOpen; @@ -1238,7 +1303,7 @@ sub openDumpTool() } if ($useValgrind) { - unshift @args, "valgrind"; + unshift @args, "valgrind", "--suppressions=$platformBaseDirectory/qt/SuppressedValgrindErrors"; } $ENV{MallocStackLogging} = 1 if $shouldCheckLeaks; @@ -1326,11 +1391,9 @@ sub openHTTPDIfNeeded() my $jsTestResourcesDirectory = $testDirectory . "/fast/js/resources"; my $typesConfig = "$testDirectory/http/conf/mime.types"; my $listen = "127.0.0.1:$httpdPort"; - my $absTestResultsDirectory = File::Spec->rel2abs(glob $testResultsDirectory); + my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory(); my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; - mkpath $absTestResultsDirectory; - my @args = ( "-f", "$httpdConfig", "-C", "DocumentRoot \"$documentRoot\"", @@ -1373,6 +1436,77 @@ sub closeHTTPD() $isHttpdOpen = 0; } +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 $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", + "-l", "$logFile", + ); + # 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) = @_; @@ -1980,8 +2114,10 @@ sub fileShouldBeIgnored return 0; } -sub readSkippedFiles +sub readSkippedFiles($) { + my ($constraintPath) = @_; + foreach my $level (@platformTestHierarchy) { if (open SKIPPED, "<", "$level/Skipped") { if ($verbose) { @@ -1996,8 +2132,19 @@ sub readSkippedFiles $skipped =~ s/[ \n\r]+$//; if ($skipped && $skipped !~ /^#/) { if ($skippedOnly) { - if (!&fileShouldBeIgnored($skipped)) { - push(@ARGV, $skipped); + 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"; } @@ -2068,6 +2215,9 @@ sub findTestsToRun } } + # Remove duplicate tests + @testsToRun = keys %{{ map { $_ => 1 } @testsToRun }}; + @testsToRun = sort pathcmp @testsToRun; # Reverse the tests diff --git a/WebKitTools/Scripts/run-webkit-unittests b/WebKitTools/Scripts/run-webkit-unittests index 8d0ef1d..3487299 100755 --- a/WebKitTools/Scripts/run-webkit-unittests +++ b/WebKitTools/Scripts/run-webkit-unittests @@ -32,11 +32,18 @@ import unittest from modules.bugzilla_unittest import * from modules.buildbot_unittest import * from modules.changelogs_unittest import * +from modules.commands.download_unittest import * +from modules.commands.upload_unittest import * +from modules.commands.queries_unittest import * +from modules.commands.queues_unittest import * from modules.committers_unittest import * from modules.cpp_style_unittest import * from modules.diff_parser_unittest import * from modules.logging_unittest import * +from modules.multicommandtool_unittest import * from modules.scm_unittest import * +from modules.webkitport_unittest import * +from modules.workqueue_unittest import * if __name__ == "__main__": unittest.main() diff --git a/WebKitTools/Scripts/run-webkit-websocketserver b/WebKitTools/Scripts/run-webkit-websocketserver new file mode 100755 index 0000000..e05303a --- /dev/null +++ b/WebKitTools/Scripts/run-webkit-websocketserver @@ -0,0 +1,96 @@ +#!/usr/bin/perl +# 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. + +# Script to run Web Socket server. + +use strict; +use warnings; + +use File::Spec; +use FindBin; +use IPC::Open2; + +use lib $FindBin::Bin; +use webkitdirs; + +sub closeWebSocketServer(); +sub openWebSocketServer(); + +my $webSocketPort = 8880; + +my $srcDir = sourceDir(); +my $layoutTestsName = "$srcDir/LayoutTests"; +my $testDirectory = File::Spec->rel2abs($layoutTestsName); +my $webSocketServerPID = 0; + + +print "Starting Web Socket server...\n"; +openWebSocketServer(); +print "Started.\n"; +print "Hit [ENTER] to stop it."; +<STDIN>; +print "Stopping Web Socket server...\n"; +closeWebSocketServer(); +print "Stopped.\n"; +exit 0; + + +sub openWebSocketServer() +{ + my $webSocketServerPath = "/usr/bin/python"; + my $webSocketPythonPath = "$srcDir/WebKitTools/pywebsocket"; + my $webSocketHandlerDir = "$testDirectory"; + my $webSocketHandlerScanDir = "$testDirectory/websocket/tests"; + + my @args = ( + "$srcDir/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py", + "-p", "$webSocketPort", + "-d", "$webSocketHandlerDir", + "-s", "$webSocketHandlerScanDir", + ); + + $ENV{"PYTHONPATH"} = $webSocketPythonPath; + $webSocketServerPID = open2(\*WEBSOCKETSERVER_IN, \*WEBSOCKETSERVER_OUT, $webSocketServerPath, @args); + + my $listen = "http://127.0.0.1:$webSocketPort"; + my $retryCount = 10; + while (system("/usr/bin/curl -k -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + sleep 1; + --$retryCount; + } + die "Timed out waiting for WebSocketServer to start" unless $retryCount; +} + +sub closeWebSocketServer() +{ + close WEBSOCKETSERVER_IN; + close WEBSOCKETSERVER_OUT; + kill 15, $webSocketServerPID; +} + diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply index 7d14e3a..0373aa5 100755 --- a/WebKitTools/Scripts/svn-apply +++ b/WebKitTools/Scripts/svn-apply @@ -55,7 +55,7 @@ # Notice a patch that's being applied at the "wrong level" and make it work anyway. # Do a dry run on the whole patch and don't do anything if part of the patch is # going to fail (probably too strict unless we exclude ChangeLog). -# Handle git-diff patches with binary changes +# Handle git-diff patches with binary delta use strict; use warnings; @@ -75,11 +75,11 @@ sub addDirectoriesIfNeeded($); sub applyPatch($$;$); sub checksum($); sub handleBinaryChange($$); +sub handleGitBinaryChange($$); sub isDirectoryEmptyForRemoval($); sub patch($); sub removeDirectoriesIfNeeded(); sub setChangeLogDateAndReviewer($$); -sub removeEOL($); # These should be replaced by an scm class/module: sub scmKnowsOfFile($); @@ -277,6 +277,39 @@ sub handleBinaryChange($$) } } +sub handleGitBinaryChange($$) +{ + my ($fullPath, $contents) = @_; + + my ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk) = decodeGitBinaryPatch($contents, $fullPath); + # FIXME: support "delta" type. + die "only literal type is supported now" if ($binaryChunkType ne "literal" || $reverseBinaryChunkType ne "literal"); + + my $isFileAddition = $contents =~ /\nnew file mode \d+\n/; + my $isFileDeletion = $contents =~ /\ndeleted file mode \d+\n/; + + my $originalContents = ""; + if (open FILE, $fullPath) { + die "$fullPath already exists" if $isFileAddition; + + $originalContents = join("", <FILE>); + close FILE; + } + die "Original content of $fullPath mismatches" if $originalContents ne $reverseBinaryChunk; + + if ($isFileDeletion) { + scmRemove($fullPath); + } else { + # Addition or Modification + open FILE, ">", $fullPath or die "Failed to open $fullPath."; + print FILE $binaryChunk; + close FILE; + if ($isFileAddition) { + scmAdd($fullPath); + } + } +} + sub isDirectoryEmptyForRemoval($) { my ($dir) = @_; @@ -311,12 +344,14 @@ sub patch($) my $deletion = 0; my $addition = 0; my $isBinary = 0; + my $isGitBinary = 0; $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\r?\n/ || $patch =~ /\n@@ -0,0 .* @@/) && !exists($copiedFiles{$fullPath}); $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./; + $isGitBinary = 1 if $patch =~ /\nGIT binary patch\n/; - if (!$addition && !$deletion && !$isBinary) { + if (!$addition && !$deletion && !$isBinary && !$isGitBinary) { # Standard patch, patch tool can handle this. if (basename($fullPath) eq "ChangeLog") { my $changeLogDotOrigExisted = -f "${fullPath}.orig"; @@ -333,6 +368,9 @@ sub patch($) if ($isBinary) { # Binary change handleBinaryChange($fullPath, $patch); + } elsif ($isGitBinary) { + # Git binary change + handleGitBinaryChange($fullPath, $patch); } elsif ($deletion) { # Deletion applyPatch($patch, $fullPath, ["--force"]); @@ -378,14 +416,6 @@ sub setChangeLogDateAndReviewer($$) return $patch; } -sub removeEOL($) -{ - my ($line) = @_; - - $line =~ s/[\r\n]+$//g; - return $line; -} - # This could be made into a more general "status" call, except svn and git # have different ideas about "moving" files which might get confusing. sub scmWillDeleteFile($) diff --git a/WebKitTools/Scripts/svn-unapply b/WebKitTools/Scripts/svn-unapply index 94bb1ce..c277a3e 100755 --- a/WebKitTools/Scripts/svn-unapply +++ b/WebKitTools/Scripts/svn-unapply @@ -73,7 +73,6 @@ use VCSUtils; sub checksum($); sub patch($); sub revertDirectories(); -sub removeEOL($); sub unapplyPatch($$;$); sub unsetChangeLogDate($$); @@ -259,14 +258,6 @@ sub revertDirectories() } } -sub removeEOL($) -{ - my ($line) = @_; - - $line =~ s/[\r\n]+$//g; - return $line; -} - sub unapplyPatch($$;$) { my ($patch, $fullPath, $options) = @_; diff --git a/WebKitTools/Scripts/update-webkit b/WebKitTools/Scripts/update-webkit index b503004..7602c41 100755 --- a/WebKitTools/Scripts/update-webkit +++ b/WebKitTools/Scripts/update-webkit @@ -39,6 +39,7 @@ use VCSUtils; use webkitdirs; sub runSvnUpdate(); +sub runGitUpdate(); # Handle options my $quiet = ''; @@ -46,6 +47,11 @@ my $showHelp; determineIsChromium(); +chdirWebKit(); + +my $isGit = isGit(); +my $isSVN = isSVN(); + my $getOptionsResult = GetOptions( 'h|help' => \$showHelp, 'q|quiet' => \$quiet, @@ -67,20 +73,23 @@ push @svnOptions, '-q' if $quiet; # Don't prompt when using svn-1.6 or newer. push @svnOptions, qw(--accept postpone) if isSVNVersion16OrNewer(); -chdirWebKit(); print "Updating OpenSource\n" unless $quiet; -runSvnUpdate(); +runSvnUpdate() if $isSVN; +runGitUpdate() if $isGit; if (-d "../Internal") { chdir("../Internal"); print "Updating Internal\n" unless $quiet; - runSvnUpdate(); + runSvnUpdate() if $isSVN; + runGitUpdate() if $isGit; } elsif (isChromium()) { system("perl", "WebKitTools/Scripts/update-webkit-chromium") == 0 or die $!; } elsif (isAppleWinWebKit()) { system("perl", "WebKitTools/Scripts/update-webkit-auxiliary-libs") == 0 or die; } +setupAppleWinEnv() if isAppleWinWebKit(); + exit 0; sub runSvnUpdate() @@ -104,3 +113,8 @@ sub runSvnUpdate() or die "Could not open resolve-ChangeLogs script: $!.\n"; } } + +sub runGitUpdate() +{ + system("git", "svn", "rebase") == 0 or die; +} diff --git a/WebKitTools/Scripts/update-webkit-chromium b/WebKitTools/Scripts/update-webkit-chromium index a0cc19a..779b9a6 100644..100755 --- a/WebKitTools/Scripts/update-webkit-chromium +++ b/WebKitTools/Scripts/update-webkit-chromium @@ -48,4 +48,4 @@ if (! -e ".gclient") { # Execute gclient sync. print "Updating chromium port dependencies using gclient...\n"; -system("gclient", "sync") == 0 or die $!; +system("gclient", "sync", "--force") == 0 or die $!; diff --git a/WebKitTools/Scripts/validate-committer-lists b/WebKitTools/Scripts/validate-committer-lists new file mode 100755 index 0000000..05f2b36 --- /dev/null +++ b/WebKitTools/Scripts/validate-committer-lists @@ -0,0 +1,252 @@ +#!/usr/bin/env python + +# 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. +# +# Checks Python's known list of committers against lists.webkit.org and SVN history. + + +import os +import subprocess +import re +import urllib2 +from datetime import date, datetime, timedelta +from modules.committers import CommitterList +from modules.logging import log, error + +# WebKit includes a built copy of BeautifulSoup in Scripts/modules +# so this import should always succeed. +from modules.BeautifulSoup import BeautifulSoup + +def print_list_if_non_empty(title, list_to_print): + if not list_to_print: + return + print # Newline before the list + print title + for item in list_to_print: + print item + +class CommitterListFromMailingList: + committers_list_url = "http://lists.webkit.org/mailman/roster.cgi/webkit-committers" + reviewers_list_url = "http://lists.webkit.org/mailman/roster.cgi/webkit-reviewers" + + def _fetch_emails_from_page(self, url): + page = urllib2.urlopen(url) + soup = BeautifulSoup(page) + + emails = [] + # Grab the cells in the first column (which happens to be the bug ids). + for email_item in soup('li'): + email_link = email_item.find("a") + email = email_link.string.replace(" at ", "@") # The email is obfuscated using " at " instead of "@". + emails.append(email) + return emails + + @staticmethod + def _commiters_not_found_in_email_list(committers, emails): + missing_from_mailing_list = [] + for committer in committers: + for email in committer.emails: + if email in emails: + break + else: + missing_from_mailing_list.append(committer) + return missing_from_mailing_list + + @staticmethod + def _emails_not_found_in_committer_list(committers, emails): + email_to_committer_map = {} + for committer in committers: + for email in committer.emails: + email_to_committer_map[email] = committer + + return filter(lambda email: not email_to_committer_map.get(email), emails) + + def check_for_emails_missing_from_list(self, committer_list): + committer_emails = self._fetch_emails_from_page(self.committers_list_url) + list_name = "webkit-committers@lists.webkit.org" + + missing_from_mailing_list = self._commiters_not_found_in_email_list(committer_list.committers(), committer_emails) + print_list_if_non_empty("Committers missing from %s:" % list_name, missing_from_mailing_list) + + users_missing_from_committers = self._emails_not_found_in_committer_list(committer_list.committers(), committer_emails) + print_list_if_non_empty("Subcribers to %s missing from committer.py:" % list_name, users_missing_from_committers) + + + reviewer_emails = self._fetch_emails_from_page(self.reviewers_list_url) + list_name = "webkit-reviewers@lists.webkit.org" + + missing_from_mailing_list = self._commiters_not_found_in_email_list(committer_list.reviewers(), reviewer_emails) + print_list_if_non_empty("Reviewers missing from %s:" % list_name, missing_from_mailing_list) + + missing_from_reviewers = self._emails_not_found_in_committer_list(committer_list.reviewers(), reviewer_emails) + print_list_if_non_empty("Subcribers to %s missing from reviewers in committer.py:" % list_name, missing_from_reviewers) + + missing_from_committers = self._emails_not_found_in_committer_list(committer_list.committers(), reviewer_emails) + print_list_if_non_empty("Subcribers to %s completely missing from committers.py" % list_name, missing_from_committers) + + +class CommitterListFromGit: + login_to_email_address = { + 'aliceli1' : 'alice.liu@apple.com', + 'bdash' : 'mrowe@apple.com', + 'bdibello' : 'bdibello@apple.com', # Bruce DiBello, only 4 commits: r10023, r9548, r9538, r9535 + 'cblu' : 'cblu@apple.com', + 'cpeterse' : 'cpetersen@apple.com', + 'eseidel' : 'eric@webkit.org', + 'gdennis' : 'gdennis@webkit.org', + 'goldsmit' : 'goldsmit@apple.com', # Debbie Goldsmith, only one commit r8839 + 'gramps' : 'gramps@apple.com', + 'honeycutt' : 'jhoneycutt@apple.com', + 'jdevalk' : 'joost@webkit.org', + 'jens' : 'jens@apple.com', + 'justing' : 'justin.garcia@apple.com', + 'kali' : 'kali@apple.com', # Christy Warren, did BIDI work, 5 commits: r8815, r8802, r8801, r8791, r8773, r8603 + 'kjk' : 'kkowalczyk@gmail.com', + 'kmccullo' : 'kmccullough@apple.com', + 'kocienda' : 'kocienda@apple.com', + 'lamadio' : 'lamadio@apple.com', # Lou Amadio, only 2 commits: r17949 and r17783 + 'lars' : 'lars@kde.org', + 'lweintraub' : 'lweintraub@apple.com', + 'lypanov' : 'lypanov@kde.org', + 'mhay' : 'mhay@apple.com', # Mike Hay, 3 commits: r3813, r2552, r2548 + 'ouch' : 'ouch@apple.com', # John Louch + 'pyeh' : 'patti@apple.com', # Patti Yeh, did VoiceOver work in WebKit + 'rjw' : 'rjw@apple.com', + 'seangies' : 'seangies@apple.com', # Sean Gies?, only 5 commits: r16600, r16592, r16511, r16489, r16484 + 'sheridan' : 'sheridan@apple.com', # Shelly Sheridan + 'thatcher' : 'timothy@apple.com', + 'tomernic' : 'timo@apple.com', + 'trey' : 'trey@usa.net', + 'tristan' : 'tristan@apple.com', + 'vicki' : 'vicki@apple.com', + 'voas' : 'voas@apple.com', # Ed Voas, did some Carbon work in WebKit + 'zack' : 'zack@kde.org', + 'zimmermann' : 'zimmermann@webkit.org', + } + + def __init__(self): + self._last_commit_time_by_author_cache = {} + + def _fetch_authors_and_last_commit_time_from_git_log(self): + last_commit_dates = {} + git_log_args = ['git', 'log', '--reverse', '--pretty=format:%ae %at'] + process = subprocess.Popen(git_log_args, stdout=subprocess.PIPE) + + # eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc 1257090899 + line_regexp = re.compile("^(?P<author>.+)@\S+ (?P<timestamp>\d+)$") + while True: + output_line = process.stdout.readline() + if output_line == '' and process.poll() != None: + return last_commit_dates + + match_result = line_regexp.match(output_line) + if not match_result: + error("Failed to match line: %s" % output_line) + last_commit_dates[match_result.group('author')] = float(match_result.group('timestamp')) + + def _fill_in_emails_for_old_logins(self): + authors_missing_email = filter(lambda author: author.find('@') == -1, self._last_commit_time_by_author_cache) + authors_with_email = filter(lambda author: author.find('@') != -1, self._last_commit_time_by_author_cache) + prefixes_of_authors_with_email = map(lambda author: author.split('@')[0], authors_with_email) + + for author in authors_missing_email: + # First check to see if we have a manual mapping from login to email. + author_email = self.login_to_email_address.get(author) + + # Most old logins like 'darin' are now just 'darin@apple.com', so check for a prefix match if a manual mapping was not found. + if not author_email and author in prefixes_of_authors_with_email: + author_email_index = prefixes_of_authors_with_email.index(author) + author_email = authors_with_email[author_email_index] + + if not author_email: + # No known email mapping, likely not an active committer. We could log here. + continue + + # log("%s -> %s" % (author, author_email)) # For sanity checking. + no_email_commit_time = self._last_commit_time_by_author_cache.get(author) + email_commit_time = self._last_commit_time_by_author_cache.get(author_email) + # We compare the timestamps for extra sanity even though we could assume commits before email address were used for login are always going to be older. + if not email_commit_time or email_commit_time < no_email_commit_time: + self._last_commit_time_by_author_cache[author_email] = no_email_commit_time + del self._last_commit_time_by_author_cache[author] + + def _last_commit_by_author(self): + if not self._last_commit_time_by_author_cache: + self._last_commit_time_by_author_cache = self._fetch_authors_and_last_commit_time_from_git_log() + self._fill_in_emails_for_old_logins() + del self._last_commit_time_by_author_cache['(no author)'] # The initial svn import isn't very useful. + return self._last_commit_time_by_author_cache + + @staticmethod + def _print_three_column_row(widths, values): + print "%s%s%s" % (values[0].ljust(widths[0]), values[1].ljust(widths[1]), values[2]) + + def print_possibly_expired_committers(self, committer_list): + authors_and_last_commits = self._last_commit_by_author().items() + authors_and_last_commits.sort(lambda a,b: cmp(a[1], b[1]), reverse=True) + committer_cuttof = date.today() - timedelta(days=365) + column_widths = [13, 25] + print + print "Committers who have not committed within one year:" + self._print_three_column_row(column_widths, ("Last Commit", "Committer Email", "Committer Record")) + for (author, last_commit) in authors_and_last_commits: + last_commit_date = date.fromtimestamp(last_commit) + if committer_cuttof > last_commit_date: + committer_record = committer_list.committer_by_email(author) + self._print_three_column_row(column_widths, (str(last_commit_date), author, committer_record)) + + def print_committers_missing_from_committer_list(self, committer_list): + missing_from_committers_py = [] + last_commit_time_by_author = self._last_commit_by_author() + for author in last_commit_time_by_author: + if not committer_list.committer_by_email(author): + missing_from_committers_py.append(author) + + never_committed = [] + for committer in committer_list.committers(): + for email in committer.emails: + if last_commit_time_by_author.get(email): + break + else: + never_committed.append(committer) + + print_list_if_non_empty("Historical committers missing from committer.py:", missing_from_committers_py) + print_list_if_non_empty("Committers in committer.py who have never committed:", never_committed) + + +def main(): + committer_list = CommitterList() + CommitterListFromMailingList().check_for_emails_missing_from_list(committer_list) + + svn_committer_list = CommitterListFromGit() + svn_committer_list.print_possibly_expired_committers(committer_list) + svn_committer_list.print_committers_missing_from_committer_list(committer_list) + +if __name__ == "__main__": + main() diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm index 16f9c26..64e5dc4 100644 --- a/WebKitTools/Scripts/webkitdirs.pm +++ b/WebKitTools/Scripts/webkitdirs.pm @@ -67,6 +67,7 @@ my $isChromium; # Variables for Win32 support my $vcBuildPath; my $windowsTmpPath; +my $windowsSourceDir; sub determineSourceDir { @@ -521,9 +522,19 @@ sub builtDylibPathForName { my $libraryName = shift; determineConfigurationProductDir(); - if (isQt() or isChromium()) { + if (isChromium()) { return "$configurationProductDir/$libraryName"; } + if (isQt()) { + $libraryName = "QtWebKit"; + if (isDarwin() and -d "$configurationProductDir/lib/$libraryName.framework") { + return "$configurationProductDir/lib/$libraryName.framework/$libraryName"; + } elsif (isWindows() or isCygwin()) { + return "$configurationProductDir/lib/$libraryName.dll"; + } else { + return "$configurationProductDir/lib/lib$libraryName.so"; + } + } if (isWx()) { return "$configurationProductDir/libwxwebkit.dylib"; } @@ -561,7 +572,7 @@ sub libraryContainsSymbol my $path = shift; my $symbol = shift; - if (isCygwin()) { + if (isCygwin() or isWindows()) { # FIXME: Implement this for Windows. return 0; } @@ -637,7 +648,8 @@ sub checkWebCoreSVGSupport sub hasAcceleratedCompositingSupport { - return 0 if isCygwin() || isQt(); + # On platforms other than Mac the Skipped files are used to skip compositing tests + return 1 if !isAppleMacWebKit(); my $path = shift; return libraryContainsSymbol($path, "GraphicsLayer"); @@ -657,7 +669,8 @@ sub checkWebCoreAcceleratedCompositingSupport sub has3DRenderingSupport { - return 0 if isQt(); + # On platforms other than Mac the Skipped files are used to skip 3D tests + return 1 if !isAppleMacWebKit(); my $path = shift; return libraryContainsSymbol($path, "WebCoreHas3DRendering"); @@ -680,7 +693,7 @@ sub has3DCanvasSupport return 0 if isQt(); my $path = shift; - return libraryContainsSymbol($path, "CanvasShader"); + return libraryContainsSymbol($path, "WebGLShader"); } sub checkWebCore3DCanvasSupport @@ -974,6 +987,11 @@ sub isSnowLeopard() return isDarwin() && osXVersion()->{"minor"} == 6; } +sub isWindowsNT() +{ + return $ENV{'OS'} eq 'Windows_NT'; +} + sub relativeScriptsDir() { my $scriptDir = File::Spec->catpath("", File::Spec->abs2rel(dirname($0), getcwd()), ""); @@ -1025,7 +1043,7 @@ sub checkRequiredSystemConfig print "http://developer.apple.com/tools/xcode\n"; print "*************************************************************\n"; } - } elsif (isGtk() or isQt() or isWx() or isChromium()) { + } elsif (isGtk() or isQt() or isWx()) { my @cmds = qw(flex bison gperf); my @missing = (); foreach my $cmd (@cmds) { @@ -1041,6 +1059,68 @@ sub checkRequiredSystemConfig # Win32 and other platforms may want to check for minimum config } +sub determineWindowsSourceDir() +{ + return if $windowsSourceDir; + my $sourceDir = sourceDir(); + chomp($windowsSourceDir = `cygpath -w $sourceDir`); +} + +sub windowsSourceDir() +{ + determineWindowsSourceDir(); + return $windowsSourceDir; +} + +sub windowsLibrariesDir() +{ + return windowsSourceDir() . "\\WebKitLibraries\\win"; +} + +sub windowsOutputDir() +{ + return windowsSourceDir() . "\\WebKitBuild"; +} + +sub setupAppleWinEnv() +{ + return unless isAppleWinWebKit(); + + if (isWindowsNT()) { + my $restartNeeded = 0; + my %variablesToSet = (); + + # Setting the environment variable 'CYGWIN' to 'tty' makes cygwin enable extra support (i.e., termios) + # for UNIX-like ttys in the Windows console + $variablesToSet{CYGWIN} = "tty" unless $ENV{CYGWIN}; + + # Those environment variables must be set to be able to build inside Visual Studio. + $variablesToSet{WEBKITLIBRARIESDIR} = windowsLibrariesDir() unless $ENV{WEBKITLIBRARIESDIR}; + $variablesToSet{WEBKITOUTPUTDIR} = windowsOutputDir() unless $ENV{WEBKITOUTPUTDIR}; + + foreach my $variable (keys %variablesToSet) { + print "Setting the Environment Variable '" . $variable . "' to '" . $variablesToSet{$variable} . "'\n\n"; + system qw(regtool -s set), '\\HKEY_CURRENT_USER\\Environment\\' . $variable, $variablesToSet{$variable}; + $restartNeeded ||= $variable eq "WEBKITLIBRARIESDIR" || $variable eq "WEBKITOUTPUTDIR"; + } + + if ($restartNeeded) { + print "Please restart your computer before attempting to build inside Visual Studio.\n\n"; + } + } else { + if (!$ENV{'WEBKITLIBRARIESDIR'}) { + print "Warning: You must set the 'WebKitLibrariesDir' environment variable\n"; + print " to be able build WebKit from within Visual Studio.\n"; + print " Make sure that 'WebKitLibrariesDir' points to the\n"; + print " 'WebKitLibraries/win' directory, not the 'WebKitLibraries/' directory.\n\n"; + } + if (!$ENV{'WEBKITOUTPUTDIR'}) { + print "Warning: You must set the 'WebKitOutputDir' environment variable\n"; + print " to be able build WebKit from within Visual Studio.\n\n"; + } + } +} + sub setupCygwinEnv() { return if !isCygwin(); @@ -1119,9 +1199,6 @@ sub buildVisualStudioProject } my $useenv = "/useenv"; - if (isChromium()) { - $useenv = ""; - } my @command = ($vcBuildPath, $useenv, $winProjectPath, $action, $config); @@ -1393,6 +1470,46 @@ sub buildGtkProject($$@) return buildAutotoolsProject($clean, @buildArgs); } +sub buildChromiumMakefile($$$) +{ + my ($dir, $target, $clean) = @_; + chdir $dir; + if ($clean) { + return system qw(rm -rf out); + } + my $config = configuration(); + my @command = ("make", "-j4", "BUILDTYPE=$config", $target); + print join(" ", @command) . "\n"; + return system @command; +} + +sub buildChromiumVisualStudioProject($$) +{ + my ($projectPath, $clean) = @_; + + my $config = configuration(); + my $action = "/build"; + $action = "/clean" if $clean; + + # Find Visual Studio installation. + my $vsInstallDir; + my $programFilesPath = $ENV{'PROGRAMFILES'} || "C:\\Program Files"; + if ($ENV{'VSINSTALLDIR'}) { + $vsInstallDir = $ENV{'VSINSTALLDIR'}; + } else { + $vsInstallDir = "$programFilesPath/Microsoft Visual Studio 8"; + } + $vsInstallDir = `cygpath "$vsInstallDir"` if isCygwin(); + chomp $vsInstallDir; + $vcBuildPath = "$vsInstallDir/Common7/IDE/devenv.com"; + + # Create command line and execute it. + my @command = ($vcBuildPath, $projectPath, $action, $config); + print "Building results into: ", baseProductDir(), "\n"; + print join(" ", @command), "\n"; + return system @command; +} + sub buildChromium($@) { my ($clean, @options) = @_; @@ -1400,30 +1517,38 @@ sub buildChromium($@) my $result = 1; if (isDarwin()) { # Mac build - builds the root xcode project. - $result = buildXCodeProject("WebKit/chromium/webkit", - $clean, - (@options)); - } elsif (isCygwin()) { + $result = buildXCodeProject("WebKit/chromium/WebKit", $clean, (@options)); + } elsif (isCygwin() || isWindows()) { # Windows build - builds the root visual studio solution. - $result = buildVisualStudioProject("WebKit/chromium/webkit.sln", - $clean); + $result = buildChromiumVisualStudioProject("WebKit/chromium/WebKit.sln", $clean); } elsif (isLinux()) { - # Linux build - # FIXME support linux. - print STDERR "Linux build is not supported. Yet."; + # Linux build - build using make. + $ result = buildChromiumMakefile("WebKit/chromium/", "webkit", $clean); } else { - print STDERR "This platform is not supported by chromium."; + print STDERR "This platform is not supported by chromium.\n"; } return $result; } +sub appleApplicationSupportPath +{ + open INSTALL_DIR, "</proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Apple\ Inc./Apple\ Application\ Support/InstallDir"; + my $path = <INSTALL_DIR>; + $path =~ s/[\r\n\x00].*//; + close INSTALL_DIR; + + my $unixPath = `cygpath -u '$path'`; + chomp $unixPath; + return $unixPath; +} + sub setPathForRunningWebKitApp { my ($env) = @_; return unless isAppleWinWebKit(); - $env->{PATH} = join(':', productDir(), dirname(installedSafariPath()), $env->{PATH} || ""); + $env->{PATH} = join(':', productDir(), dirname(installedSafariPath()), appleApplicationSupportPath(), $env->{PATH} || ""); } sub exitStatus($) diff --git a/WebKitTools/pywebsocket/example/echo_client.py b/WebKitTools/pywebsocket/example/echo_client.py index 61b129c..3262a6d 100644 --- a/WebKitTools/pywebsocket/example/echo_client.py +++ b/WebKitTools/pywebsocket/example/echo_client.py @@ -46,6 +46,8 @@ import socket import sys +_TIMEOUT_SEC = 10 + _DEFAULT_PORT = 80 _DEFAULT_SECURE_PORT = 443 _UNDEFINED_PORT = -1 @@ -57,6 +59,8 @@ _EXPECTED_RESPONSE = ( _UPGRADE_HEADER + _CONNECTION_HEADER) +_GOODBYE_MESSAGE = 'Goodbye' + def _method_line(resource): return 'GET %s HTTP/1.1\r\n' % resource @@ -96,13 +100,14 @@ class EchoClient(object): Shake hands and then repeat sending message and receiving its echo. """ self._socket = socket.socket() + self._socket.settimeout(self._options.socket_timeout) try: self._socket.connect((self._options.server_host, self._options.server_port)) if self._options.use_tls: self._socket = _TLSSocket(self._socket) self._handshake() - for line in self._options.message.split(','): + for line in self._options.message.split(',') + [_GOODBYE_MESSAGE]: frame = '\x00' + line.encode('utf-8') + '\xff' self._socket.send(frame) if self._options.verbose: @@ -111,7 +116,8 @@ class EchoClient(object): if received != frame: raise Exception('Incorrect echo: %r' % received) if self._options.verbose: - print 'Recv: %s' % received[1:-1].decode('utf-8') + print 'Recv: %s' % received[1:-1].decode('utf-8', + 'replace') finally: self._socket.close() @@ -166,11 +172,17 @@ def main(): parser.add_option('-r', '--resource', dest='resource', type='string', default='/echo', help='resource path') parser.add_option('-m', '--message', dest='message', type='string', - help='comma-separated messages to send') + help=('comma-separated messages to send excluding "%s" ' + 'that is always sent at the end' % + _GOODBYE_MESSAGE)) parser.add_option('-q', '--quiet', dest='verbose', action='store_false', default=True, help='suppress messages') parser.add_option('-t', '--tls', dest='use_tls', action='store_true', default=False, help='use TLS (wss://)') + parser.add_option('-k', '--socket_timeout', dest='socket_timeout', + type='int', default=_TIMEOUT_SEC, + help='Timeout(sec) for sockets') + (options, unused_args) = parser.parse_args() # Default port number depends on whether TLS is used. diff --git a/WebKitTools/pywebsocket/example/echo_wsh.py b/WebKitTools/pywebsocket/example/echo_wsh.py index f680fa5..50cad31 100644 --- a/WebKitTools/pywebsocket/example/echo_wsh.py +++ b/WebKitTools/pywebsocket/example/echo_wsh.py @@ -31,6 +31,9 @@ from mod_pywebsocket import msgutil +_GOODBYE_MESSAGE = 'Goodbye' + + def web_socket_do_extra_handshake(request): pass # Always accept. @@ -39,6 +42,8 @@ def web_socket_transfer_data(request): while True: line = msgutil.receive_message(request) msgutil.send_message(request, line) + if line == _GOODBYE_MESSAGE: + return # vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py b/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py index beacc9e..05e80e8 100644 --- a/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py +++ b/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py @@ -96,6 +96,9 @@ web_socket_transfer_data is called after the handshake completed successfully. A handler can receive/send messages from/to the client using request. mod_pywebsocket.msgutil module provides utilities for data transfer. + +A Web Socket handler must be thread-safe if the server (Apache or +standalone.py) is configured to use threads. """ diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py b/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py index 84422eb..bf9a856 100644 --- a/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py +++ b/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py @@ -62,7 +62,7 @@ def _normalize_path(path): """ path = path.replace('\\', os.path.sep) - path = os.path.abspath(path) + path = os.path.realpath(path) path = path.replace('\\', '/') return path @@ -136,7 +136,8 @@ class Dispatcher(object): self._source_warnings = [] if scan_dir is None: scan_dir = root_dir - if not os.path.abspath(scan_dir).startswith(os.path.abspath(root_dir)): + if not os.path.realpath(scan_dir).startswith( + os.path.realpath(root_dir)): raise DispatchError('scan_dir:%s must be a directory under ' 'root_dir:%s.' % (scan_dir, root_dir)) self._source_files_in_dir(root_dir, scan_dir) @@ -159,9 +160,13 @@ class Dispatcher(object): do_extra_handshake_, unused_transfer_data = self._handler(request) try: do_extra_handshake_(request) - except Exception: - raise DispatchError('%s raised exception: %s' % - (_DO_EXTRA_HANDSHAKE_HANDLER_NAME, util.get_stack_trace())) + except Exception, e: + util.prepend_message_to_exception( + '%s raised exception for %s: ' % ( + _DO_EXTRA_HANDSHAKE_HANDLER_NAME, + request.ws_resource), + e) + raise def transfer_data(self, request): """Let a handler transfer_data with a Web Socket client. @@ -176,19 +181,23 @@ class Dispatcher(object): unused_do_extra_handshake, transfer_data_ = self._handler(request) try: transfer_data_(request) - except Exception: - raise DispatchError('%s raised exception: %s' % - (_TRANSFER_DATA_HANDLER_NAME, util.get_stack_trace())) + except Exception, e: + util.prepend_message_to_exception( + '%s raised exception for %s: ' % ( + _TRANSFER_DATA_HANDLER_NAME, request.ws_resource), + e) + raise def _handler(self, request): try: - return self._handlers[request.ws_resource] + ws_resource_path = request.ws_resource.split('?', 1)[0] + return self._handlers[ws_resource_path] except KeyError: raise DispatchError('No handler for: %r' % request.ws_resource) def _source_files_in_dir(self, root_dir, scan_dir): """Source all the handler source files in the scan_dir directory. - + The resource path is determined relative to root_dir. """ diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py b/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py index bdb554d..90ae715 100644 --- a/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py +++ b/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py @@ -39,6 +39,31 @@ not suitable because they don't allow direct raw bytes writing/reading. import Queue import threading +import util + + +class MsgUtilException(Exception): + pass + + +def _read(request, length): + bytes = request.connection.read(length) + if not bytes: + raise MsgUtilException( + 'Failed to receive message from %r' % + (request.connection.remote_addr,)) + return bytes + + +def _write(request, bytes): + try: + request.connection.write(bytes) + except Exception, e: + util.prepend_message_to_exception( + 'Failed to send message to %r: ' % + (request.connection.remote_addr,), + e) + raise def send_message(request, message): @@ -49,7 +74,7 @@ def send_message(request, message): message: unicode string to send. """ - request.connection.write('\x00' + message.encode('utf-8') + '\xff') + _write(request, '\x00' + message.encode('utf-8') + '\xff') def receive_message(request): @@ -63,7 +88,7 @@ def receive_message(request): # Read 1 byte. # mp_conn.read will block if no bytes are available. # Timeout is controlled by TimeOut directive of Apache. - frame_type_str = request.connection.read(1) + frame_type_str = _read(request, 1) frame_type = ord(frame_type_str[0]) if (frame_type & 0x80) == 0x80: # The payload length is specified in the frame. @@ -73,7 +98,9 @@ def receive_message(request): else: # The payload is delimited with \xff. bytes = _read_until(request, '\xff') - message = bytes.decode('utf-8') + # The Web Socket protocol section 4.4 specifies that invalid + # characters must be replaced with U+fffd REPLACEMENT CHARACTER. + message = bytes.decode('utf-8', 'replace') if frame_type == 0x00: return message # Discard data of other types. @@ -82,7 +109,7 @@ def receive_message(request): def _payload_length(request): length = 0 while True: - b_str = request.connection.read(1) + b_str = _read(request, 1) b = ord(b_str[0]) length = length * 128 + (b & 0x7f) if (b & 0x80) == 0: @@ -93,7 +120,7 @@ def _payload_length(request): def _receive_bytes(request, length): bytes = [] while length > 0: - new_bytes = request.connection.read(length) + new_bytes = _read(request, length) bytes.append(new_bytes) length -= len(new_bytes) return ''.join(bytes) @@ -102,7 +129,7 @@ def _receive_bytes(request, length): def _read_until(request, delim_char): bytes = [] while True: - ch = request.connection.read(1) + ch = _read(request, 1) if ch == delim_char: break bytes.append(ch) diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py b/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py index a4c142b..6217585 100644 --- a/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py +++ b/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py @@ -38,6 +38,7 @@ Usage: python standalone.py [-p <ws_port>] [-w <websock_handlers>] [-s <scan_dir>] [-d <document_root>] + ... for other options, see _main below ... <ws_port> is the port number to use for ws:// connection. @@ -59,6 +60,7 @@ import BaseHTTPServer import SimpleHTTPServer import SocketServer import logging +import logging.handlers import optparse import os import socket @@ -73,6 +75,25 @@ except ImportError: import dispatch import handshake +import util + + +_LOG_LEVELS = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warn': logging.WARN, + 'error': logging.ERROR, + 'critical': logging.CRITICAL}; + +_DEFAULT_LOG_MAX_BYTES = 1024 * 256 +_DEFAULT_LOG_BACKUP_COUNT = 5 + + +def _print_warnings_if_any(dispatcher): + warnings = dispatcher.source_warnings() + if warnings: + for warning in warnings: + logging.warning('mod_pywebsocket: %s' % warning) class _StandaloneConnection(object): @@ -92,6 +113,14 @@ class _StandaloneConnection(object): self._request_handler.server.server_port) local_addr = property(get_local_addr) + def get_remote_addr(self): + """Getter to mimic mp_conn.remote_addr. + + Setting the property in __init__ won't work because the request + handler is not initialized yet there.""" + return self._request_handler.client_address + remote_addr = property(get_remote_addr) + def write(self, data): """Mimic mp_conn.write().""" return self._request_handler.wfile.write(data) @@ -152,6 +181,16 @@ class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): socket_ = OpenSSL.SSL.Connection(ctx, socket_) return socket_ + def handle_error(self, rquest, client_address): + """Override SocketServer.handle_error.""" + + logging.error( + ('Exception in processing request from: %r' % (client_address,)) + + '\n' + util.get_stack_trace()) + # Note: client_address is a tuple. To match it against %r, we need the + # trailing comma. + + class WebSocketRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): """SimpleHTTPRequestHandler specialized for Web Socket.""" @@ -159,15 +198,13 @@ class WebSocketRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): """Override SocketServer.StreamRequestHandler.setup.""" self.connection = self.request - self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) - self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + self.rfile = socket._fileobject(self.request, 'rb', self.rbufsize) + self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize) def __init__(self, *args, **keywords): self._request = _StandaloneRequest( self, WebSocketRequestHandler.options.use_tls) - self._dispatcher = dispatch.Dispatcher( - WebSocketRequestHandler.options.websock_handlers, - WebSocketRequestHandler.options.scan_dir) + self._dispatcher = WebSocketRequestHandler.options.dispatcher self._print_warnings_if_any() self._handshaker = handshake.Handshaker(self._request, self._dispatcher) @@ -198,12 +235,41 @@ class WebSocketRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): except dispatch.DispatchError, e: logging.warning('mod_pywebsocket: %s' % e) return False + except Exception, e: + logging.warning('mod_pywebsocket: %s' % e) + logging.info('mod_pywebsocket: %s' % util.get_stack_trace()) + return False return result + def log_request(self, code='-', size='-'): + """Override BaseHTTPServer.log_request.""" -def _main(): - logging.basicConfig() + logging.info('"%s" %s %s', + self.requestline, str(code), str(size)) + def log_error(self, *args): + """Override BaseHTTPServer.log_error.""" + + # Despite the name, this method is for warnings than for errors. + # For example, HTTP status code is logged by this method. + logging.warn('%s - %s' % (self.address_string(), (args[0] % args[1:]))) + + +def _configure_logging(options): + logger = logging.getLogger() + logger.setLevel(_LOG_LEVELS[options.log_level]) + if options.log_file: + handler = logging.handlers.RotatingFileHandler( + options.log_file, 'a', options.log_max, options.log_count) + else: + handler = logging.StreamHandler() + formatter = logging.Formatter( + "[%(asctime)s] [%(levelname)s] %(name)s: %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + +def _main(): parser = optparse.OptionParser() parser.add_option('-p', '--port', dest='port', type='int', default=handshake._DEFAULT_WEB_SOCKET_PORT, @@ -224,27 +290,51 @@ def _main(): 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', + default='', help='Log file.') + parser.add_option('--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', + default=_DEFAULT_LOG_MAX_BYTES, + help='Log maximum bytes') + parser.add_option('--log_count', dest='log_count', type='int', + default=_DEFAULT_LOG_BACKUP_COUNT, + help='Log backup count') options = parser.parse_args()[0] + os.chdir(options.document_root) + + _configure_logging(options) + if options.use_tls: if not _HAS_OPEN_SSL: - print >>sys.stderr, 'To use TLS, install pyOpenSSL.' + logging.critical('To use TLS, install pyOpenSSL.') sys.exit(1) if not options.private_key or not options.certificate: - print >>sys.stderr, ('To use TLS, specify private_key and ' - 'certificate.') + logging.critical( + 'To use TLS, specify private_key and certificate.') sys.exit(1) if not options.scan_dir: options.scan_dir = options.websock_handlers - WebSocketRequestHandler.options = options - WebSocketServer.options = options - - os.chdir(options.document_root) - - server = WebSocketServer(('', options.port), WebSocketRequestHandler) - server.serve_forever() + try: + # Share a Dispatcher among request handlers to save time for + # instantiation. Dispatcher can be shared because it is thread-safe. + options.dispatcher = dispatch.Dispatcher(options.websock_handlers, + options.scan_dir) + _print_warnings_if_any(options.dispatcher) + + WebSocketRequestHandler.options = options + WebSocketServer.options = options + + server = WebSocketServer(('', options.port), WebSocketRequestHandler) + server.serve_forever() + except Exception, e: + logging.critical(str(e)) + sys.exit(1) if __name__ == '__main__': diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/pywebsocket/mod_pywebsocket/util.py index 4835298..0ea8053 100644 --- a/WebKitTools/pywebsocket/mod_pywebsocket/util.py +++ b/WebKitTools/pywebsocket/mod_pywebsocket/util.py @@ -49,4 +49,11 @@ def get_stack_trace(): 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/pywebsocket/setup.py b/WebKitTools/pywebsocket/setup.py index 1810a6d..df05fef 100644 --- a/WebKitTools/pywebsocket/setup.py +++ b/WebKitTools/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.1', + version='0.4.3', ) diff --git a/WebKitTools/pywebsocket/test/test_dispatch.py b/WebKitTools/pywebsocket/test/test_dispatch.py index d617205..b19d706 100644 --- a/WebKitTools/pywebsocket/test/test_dispatch.py +++ b/WebKitTools/pywebsocket/test/test_dispatch.py @@ -136,8 +136,7 @@ class DispatcherTest(unittest.TestCase): dispatcher.do_extra_handshake(request) # Must not raise exception. request.ws_origin = 'http://bad.example.com' - self.assertRaises(dispatch.DispatchError, - dispatcher.do_extra_handshake, request) + self.assertRaises(Exception, dispatcher.do_extra_handshake, request) def test_transfer_data(self): dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) @@ -156,6 +155,20 @@ class DispatcherTest(unittest.TestCase): self.assertEqual('sub/plain_wsh.py is called for /sub/plain, None', request.connection.written_data()) + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = '/sub/plain?' + request.ws_protocol = None + dispatcher.transfer_data(request) + self.assertEqual('sub/plain_wsh.py is called for /sub/plain?, None', + request.connection.written_data()) + + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = '/sub/plain?q=v' + request.ws_protocol = None + dispatcher.transfer_data(request) + self.assertEqual('sub/plain_wsh.py is called for /sub/plain?q=v, None', + request.connection.written_data()) + def test_transfer_data_no_handler(self): dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) for resource in ['/blank', '/sub/non_callable', @@ -179,10 +192,8 @@ class DispatcherTest(unittest.TestCase): try: dispatcher.transfer_data(request) self.fail() - except dispatch.DispatchError, e: + except Exception, e: self.failUnless(str(e).find('Intentional') != -1) - except Exception: - self.fail() def test_scan_dir(self): disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) diff --git a/WebKitTools/pywebsocket/test/test_msgutil.py b/WebKitTools/pywebsocket/test/test_msgutil.py index b3ba539..16b88e0 100644 --- a/WebKitTools/pywebsocket/test/test_msgutil.py +++ b/WebKitTools/pywebsocket/test/test_msgutil.py @@ -71,6 +71,13 @@ class MessageTest(unittest.TestCase): # U+672c is encoded as e6,9c,ac in UTF-8 self.assertEqual(u'\u672c', msgutil.receive_message(request)) + def test_receive_message_erroneous_unicode(self): + # \x80 and \x81 are invalid as UTF-8. + request = _create_request('\x00\x80\x81\xff') + # Invalid characters should be replaced with + # U+fffd REPLACEMENT CHARACTER + self.assertEqual(u'\ufffd\ufffd', msgutil.receive_message(request)) + def test_receive_message_discard(self): request = _create_request('\x80\x06IGNORE\x00Hello\xff' '\x01DISREGARD\xff\x00World!\xff') diff --git a/WebKitTools/pywebsocket/test/test_util.py b/WebKitTools/pywebsocket/test/test_util.py index 8058b6d..83e2635 100644 --- a/WebKitTools/pywebsocket/test/test_util.py +++ b/WebKitTools/pywebsocket/test/test_util.py @@ -49,6 +49,12 @@ class UtilTest(unittest.TestCase): self.failUnless(trace.startswith('Traceback')) self.failUnless(trace.find('ZeroDivisionError') != -1) + def test_prepend_message_to_exception(self): + exc = Exception('World') + self.assertEqual('World', str(exc)) + util.prepend_message_to_exception('Hello ', exc) + self.assertEqual('Hello World', str(exc)) + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/wx/browser/wscript b/WebKitTools/wx/browser/wscript index d5246c1..a2a24e0 100644 --- a/WebKitTools/wx/browser/wscript +++ b/WebKitTools/wx/browser/wscript @@ -45,7 +45,7 @@ def build(bld): includes = ' '.join(include_paths), source = 'browser.cpp', target = 'wxBrowser', - uselib = 'WXWEBKIT WX ' + get_config(), + uselib = 'WX CURL ICU XSLT XML WXWEBKIT ' + get_config(), libpath = [output_dir], uselib_local = '', install_path = output_dir) diff --git a/WebKitTools/wx/build/build_utils.py b/WebKitTools/wx/build/build_utils.py index b767d9f..a6962e2 100644 --- a/WebKitTools/wx/build/build_utils.py +++ b/WebKitTools/wx/build/build_utils.py @@ -176,7 +176,7 @@ def get_config(wk_root): return config def svn_revision(): - if os.system("git info") == 0: + if os.system("git-svn info") == 0: info = commands.getoutput("git-svn info ../..") else: info = commands.getoutput("svn info") diff --git a/WebKitTools/wx/build/settings.py b/WebKitTools/wx/build/settings.py index 652a880..f7f75b6 100644 --- a/WebKitTools/wx/build/settings.py +++ b/WebKitTools/wx/build/settings.py @@ -337,7 +337,7 @@ def common_configure(conf): # common win libs conf.env.append_value('LIB', [ 'kernel32', 'user32','gdi32','comdlg32','winspool','winmm', - 'shell32', 'comctl32', 'ole32', 'oleaut32', 'uuid', 'advapi32', + 'shell32', 'shlwapi', 'comctl32', 'ole32', 'oleaut32', 'uuid', 'advapi32', 'wsock32', 'gdiplus', 'version']) conf.env['LIB_ICU'] = ['icudt', 'icule', 'iculx', 'icuuc', 'icuin', 'icuio', 'icutu'] diff --git a/WebKitTools/wx/packaging/build-debian-installer.py b/WebKitTools/wx/packaging/build-debian-installer.py new file mode 100644 index 0000000..5c6795d --- /dev/null +++ b/WebKitTools/wx/packaging/build-debian-installer.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +import os +import shutil +import sys + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "build"))) + +import build_utils + +script_dir = os.path.abspath(os.path.dirname(__file__)) +wxwebkit_dir = os.path.abspath(os.path.join(script_dir, "..", "..", "..", "WebKitBuild", "Debug" + build_utils.git_branch_name())) +wxwk_root = os.path.abspath(os.path.join(script_dir, "..", "..", "..")) + +try: + os.chdir(wxwk_root) + deb_dir = os.path.join(wxwk_root, 'wxwebkit') + if os.path.exists(deb_dir): + shutil.rmtree(deb_dir) + os.makedirs(deb_dir) + print "Archiving git tree..." + os.system('git archive --format=tar HEAD | gzip > %s/webkitwx_0.1.orig.tar.gz' % deb_dir) + src_root = os.path.join(deb_dir, 'webkitwx-0.1') + print "Extracting tree..." + os.makedirs(src_root) + os.chdir(src_root) + os.system('tar xzvf ../webkitwx_0.1.orig.tar.gz') + + shutil.copytree(os.path.join(script_dir, 'debian'), os.path.join(src_root, 'debian')) + + print "Building package..." + os.system('fakeroot debian/rules clean') + os.system('fakeroot debian/rules build') + os.system('debuild -i -rfakeroot -us -uc') +finally: + shutil.rmtree(os.path.join(src_root, 'debian')) diff --git a/WebKitTools/wx/packaging/debian/changelog b/WebKitTools/wx/packaging/debian/changelog new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/WebKitTools/wx/packaging/debian/changelog @@ -0,0 +1 @@ + diff --git a/WebKitTools/wx/packaging/debian/compat b/WebKitTools/wx/packaging/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/WebKitTools/wx/packaging/debian/compat @@ -0,0 +1 @@ +5 diff --git a/WebKitTools/wx/packaging/debian/control b/WebKitTools/wx/packaging/debian/control new file mode 100644 index 0000000..57d8407 --- /dev/null +++ b/WebKitTools/wx/packaging/debian/control @@ -0,0 +1,29 @@ +Source: webkitwx +Section: Python +Priority: extra +Maintainer: Kevin Ollivier <kevino@theolliviers.com> +Build-Depends: debhelper (>= 5.0.38), python-central (>= 0.6), python-all-dev, + libwxgtk2.8-dev (>= 2.8.9.2-1), python-wxgtk2.8, python-wxtools (>= 2.8.9.2-1), + wx2.8-headers (>= 2.8.9.2-1), wx2.8-i18n (>= 2.8.9.2-1), + flex, bison, gperf, automake, autoconf, libtool, dpatch, + libxslt1-dev, libcurl4-openssl-dev, + libicu-dev, libjpeg62-dev, libpng12-dev, libsqlite3-dev, libgtk2.0-dev +Build-Conflicts: python-setuptools +XS-Python-Version: all +Standards-Version: 3.7.3 + +Package: python-webkitwx +Section: python +Architecture: any +Depends: ${python:Depends}, ${shlibs:Depends} +Provides: ${python:Provides}, webkitwx +XB-Python-Version: ${python:Versions} +Description: Python binding of wxwebkit + This is an experimental packaged release of webkit for wxpython + +Package: webkitwx-headers +Architecture: all +Description: Python binding of wxwebkit + This is an experimental packaged release of webkit for wxpython + These are the header files + diff --git a/WebKitTools/wx/packaging/debian/copyright b/WebKitTools/wx/packaging/debian/copyright new file mode 100644 index 0000000..c2244ec --- /dev/null +++ b/WebKitTools/wx/packaging/debian/copyright @@ -0,0 +1,18 @@ +This package was debianized by Chris Willing c.willing@uq.edu.au on +Thu, Sat, 11 Apr 2009 12:54:52 +1000 + +Upstream Author: Kevin Ollivier kevino@theolliviers.com and others + +Copyright: 2009 Kevin Ollivier, Apple Inc., and others + +License: + LGPL + + +The Debian packaging is (C) 2009, Chris Willing <c.willing@uq.edu.au> and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. + diff --git a/WebKitTools/wx/packaging/debian/python-webkitwx.install b/WebKitTools/wx/packaging/debian/python-webkitwx.install new file mode 100644 index 0000000..dff51ce --- /dev/null +++ b/WebKitTools/wx/packaging/debian/python-webkitwx.install @@ -0,0 +1 @@ +WebKitBuild/Debug.master/libwxwebkit.so usr/lib/ diff --git a/WebKitTools/wx/packaging/debian/rules b/WebKitTools/wx/packaging/debian/rules new file mode 100644 index 0000000..6840731 --- /dev/null +++ b/WebKitTools/wx/packaging/debian/rules @@ -0,0 +1,75 @@ +#! /usr/bin/make -f + +SHELL = /bin/bash + +PYVERS := $(shell /usr/bin/python -c 'import sys; print sys.version[:3]') +VER := $(shell /usr/bin/python -c 'import sys; print sys.version[:3]') +BIULD_DIR := WebKitBuild/Debug.master +build: build-stamp +build-stamp: $(PYVERS:%=build-python%) + touch $@ +build-python%: + touch $@ + +clean: + rm -rf *-stamp build-python* build + rm -rf $(addprefix debian/,$(packages)) debian/files debian/substvars + rm -rf _trial_temp test.log + find . -name "*.pyc" |xargs -r rm + dh_clean + +install: build-stamp install-prereq $(PYVERS:%=install-python%) install-libs install-nover + +install-prereq: build-stamp + dh_testdir + dh_testroot + dh_clean -k + +install-python%: install-prereq + dh_install -ppython-webkitwx \ + $(BUILD_DIR)/webview.py \ + $(BUILD_DIR)/Debug.master/_webview.so \ + usr/lib/python$*/site-packages/wx-2.8-gtk2-unicode/wx/ + +install-nover: install-prereq + dh_install -pwebkitwx-headers \ + $(BUILD_DIR)/JavaScriptCore \ + usr/include/wxwebkit-1.0/ + +install-libs: install-prereq + dh_install + + +binary-indep: + (cd WebKitTools/Scripts && ./build-webkit --wx --makeargs="--wxpython") + dh_testdir + dh_testroot + dh_installchangelogs -i + dh_installdocs -i + dh_strip -i + dh_compress -i + dh_fixperms -i + dh_installdeb -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i + +binary-arch: + dh_testdir + dh_testroot + echo $(shell pwd) + echo $(shell ls -l) + dh_installchangelogs -a + dh_installdocs -a WebKit/wx/bindings/python/samples/simple.py + dh_strip -a + dh_compress -a -Xsimple.py + dh_fixperms -a + dh_pycentral -a + dh_installdeb -a + dh_shlibdeps -a + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install install-nover install-prereq install-libs |