summaryrefslogtreecommitdiffstats
path: root/webkit/tools
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/tools')
-rw-r--r--webkit/tools/layout_tests/layout_package/__init__.py0
-rw-r--r--webkit/tools/layout_tests/layout_package/compare_failures.py170
-rw-r--r--webkit/tools/layout_tests/layout_package/compare_failures_unittest.py305
-rw-r--r--webkit/tools/layout_tests/layout_package/http_server.py219
-rw-r--r--webkit/tools/layout_tests/layout_package/lighttpd.conf74
-rw-r--r--webkit/tools/layout_tests/layout_package/path_utils.py190
-rw-r--r--webkit/tools/layout_tests/layout_package/platform_utils.py46
-rw-r--r--webkit/tools/layout_tests/layout_package/platform_utils_win.py168
-rw-r--r--webkit/tools/layout_tests/layout_package/test_expectations.py368
-rw-r--r--webkit/tools/layout_tests/layout_package/test_failures.py215
-rw-r--r--webkit/tools/layout_tests/layout_package/test_shell_thread.py301
-rw-r--r--webkit/tools/layout_tests/layout_package/test_types_unittest.py71
-rw-r--r--webkit/tools/layout_tests/run_webkit_tests.py653
-rwxr-xr-xwebkit/tools/layout_tests/run_webkit_tests.sh9
-rw-r--r--webkit/tools/layout_tests/test_lists/README34
-rw-r--r--webkit/tools/layout_tests/test_lists/tests_fixable.txt718
-rw-r--r--webkit/tools/layout_tests/test_lists/tests_ignored.txt177
-rw-r--r--webkit/tools/layout_tests/test_types/__init__.py0
-rw-r--r--webkit/tools/layout_tests/test_types/image_diff.py133
-rw-r--r--webkit/tools/layout_tests/test_types/simplified_text_diff.py141
-rw-r--r--webkit/tools/layout_tests/test_types/test_type_base.py189
-rw-r--r--webkit/tools/layout_tests/test_types/text_diff.py95
-rw-r--r--webkit/tools/layout_tests/testdata/README2
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/form-element-geometry-actual-win.txt369
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/form-element-geometry-expected.txt365
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/null-offset-parent-actual-win.txt82
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/null-offset-parent-expected.txt82
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-actual-win.txt75
-rw-r--r--webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-expected.txt74
-rw-r--r--webkit/tools/layout_tests/testdata/expected-crashes-new-passing.txt2
-rw-r--r--webkit/tools/layout_tests/testdata/expected-crashes-new-test.txt4
-rw-r--r--webkit/tools/layout_tests/testdata/expected-crashes.txt3
-rw-r--r--webkit/tools/layout_tests/testdata/expected-failures-added.txt4
-rw-r--r--webkit/tools/layout_tests/testdata/expected-failures-new-crash.txt4
-rw-r--r--webkit/tools/layout_tests/testdata/expected-failures-new-passing.txt2
-rw-r--r--webkit/tools/layout_tests/testdata/expected-failures-new-test.txt4
-rw-r--r--webkit/tools/layout_tests/testdata/expected-failures.txt3
-rw-r--r--webkit/tools/layout_tests/testdata/expected-passing-new-baseline.txt1
-rw-r--r--webkit/tools/layout_tests/testdata/expected-passing-new-passing.txt3
-rw-r--r--webkit/tools/layout_tests/testdata/expected-passing-new-passing2.txt3
-rw-r--r--webkit/tools/layout_tests/testdata/expected-passing-new-test.txt3
-rw-r--r--webkit/tools/layout_tests/testdata/expected-passing.txt2
-rw-r--r--webkit/tools/leak_tests/run_node_leak_test.py303
-rw-r--r--webkit/tools/leak_tests/test_lists/alexa_100.txt92
-rwxr-xr-xwebkit/tools/merge/gen-merge-diff.sh98
-rw-r--r--webkit/tools/merge/update-branch-webkit.py774
-rw-r--r--webkit/tools/npapi_layout_test_plugin/PluginObject.cpp622
-rw-r--r--webkit/tools/npapi_layout_test_plugin/PluginObject.h56
-rw-r--r--webkit/tools/npapi_layout_test_plugin/SConscript100
-rw-r--r--webkit/tools/npapi_layout_test_plugin/TestObject.cpp169
-rw-r--r--webkit/tools/npapi_layout_test_plugin/TestObject.h44
-rw-r--r--webkit/tools/npapi_layout_test_plugin/main.cpp346
-rw-r--r--webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.def6
-rw-r--r--webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.rc104
-rw-r--r--webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.vcproj178
-rw-r--r--webkit/tools/npapi_layout_test_plugin/resource.h14
-rw-r--r--webkit/tools/test_shell/SConscript227
-rw-r--r--webkit/tools/test_shell/drag_delegate.cc65
-rw-r--r--webkit/tools/test_shell/drag_delegate.h60
-rw-r--r--webkit/tools/test_shell/drop_delegate.cc76
-rw-r--r--webkit/tools/test_shell/drop_delegate.h66
-rw-r--r--webkit/tools/test_shell/event_sending_controller.cc511
-rw-r--r--webkit/tools/test_shell/event_sending_controller.h108
-rw-r--r--webkit/tools/test_shell/foreground_helper.h111
-rw-r--r--webkit/tools/test_shell/image_decoder_unittest.cc211
-rw-r--r--webkit/tools/test_shell/image_decoder_unittest.h103
-rw-r--r--webkit/tools/test_shell/keyboard_unittest.cc293
-rw-r--r--webkit/tools/test_shell/layout_test_controller.cc595
-rw-r--r--webkit/tools/test_shell/layout_test_controller.h306
-rw-r--r--webkit/tools/test_shell/layout_test_controller_unittest.cc114
-rw-r--r--webkit/tools/test_shell/node_leak_test.cc113
-rw-r--r--webkit/tools/test_shell/plugin_tests.cc147
-rw-r--r--webkit/tools/test_shell/resource.h38
-rw-r--r--webkit/tools/test_shell/resources/AHEM____.TTFbin0 -> 12480 bytes
-rw-r--r--webkit/tools/test_shell/resources/README.txt24
-rw-r--r--webkit/tools/test_shell/resources/missingImage.gifbin0 -> 362 bytes
-rw-r--r--webkit/tools/test_shell/resources/small.icobin0 -> 23558 bytes
-rw-r--r--webkit/tools/test_shell/resources/test_shell.icobin0 -> 23558 bytes
-rw-r--r--webkit/tools/test_shell/resources/test_shell.rc133
-rw-r--r--webkit/tools/test_shell/run_all_tests.cc85
-rw-r--r--webkit/tools/test_shell/simple_resource_loader_bridge.cc584
-rw-r--r--webkit/tools/test_shell/simple_resource_loader_bridge.h55
-rw-r--r--webkit/tools/test_shell/temp/navigation_controller_base.cc301
-rw-r--r--webkit/tools/test_shell/temp/navigation_controller_base.h220
-rw-r--r--webkit/tools/test_shell/temp/navigation_entry.h154
-rw-r--r--webkit/tools/test_shell/temp/page_transition_types.h173
-rw-r--r--webkit/tools/test_shell/test_navigation_controller.cc107
-rw-r--r--webkit/tools/test_shell/test_navigation_controller.h118
-rw-r--r--webkit/tools/test_shell/test_shell.cc1128
-rw-r--r--webkit/tools/test_shell/test_shell.h273
-rw-r--r--webkit/tools/test_shell/test_shell.vcproj546
-rw-r--r--webkit/tools/test_shell/test_shell.vsprops22
-rw-r--r--webkit/tools/test_shell/test_shell_main.cc360
-rw-r--r--webkit/tools/test_shell/test_shell_request_context.cc69
-rw-r--r--webkit/tools/test_shell/test_shell_request_context.h53
-rw-r--r--webkit/tools/test_shell/test_shell_switches.cc76
-rw-r--r--webkit/tools/test_shell/test_shell_switches.h55
-rw-r--r--webkit/tools/test_shell/test_shell_test.cc63
-rw-r--r--webkit/tools/test_shell/test_shell_test.h63
-rw-r--r--webkit/tools/test_shell/test_shell_tests.vcproj393
-rw-r--r--webkit/tools/test_shell/test_shell_tests.vsprops17
-rw-r--r--webkit/tools/test_shell/test_webview_delegate.cc943
-rw-r--r--webkit/tools/test_shell/test_webview_delegate.h303
-rw-r--r--webkit/tools/test_shell/text_input_controller.cc249
-rw-r--r--webkit/tools/test_shell/text_input_controller.h74
-rw-r--r--webkit/tools/test_shell/text_input_controller_unittest.cc82
-rw-r--r--webkit/tools/test_shell/webview_host.cc72
-rw-r--r--webkit/tools/test_shell/webview_host.h61
-rw-r--r--webkit/tools/test_shell/webwidget_host.cc354
-rw-r--r--webkit/tools/test_shell/webwidget_host.h111
110 files changed, 18734 insertions, 0 deletions
diff --git a/webkit/tools/layout_tests/layout_package/__init__.py b/webkit/tools/layout_tests/layout_package/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/__init__.py
diff --git a/webkit/tools/layout_tests/layout_package/compare_failures.py b/webkit/tools/layout_tests/layout_package/compare_failures.py
new file mode 100644
index 0000000..5931ef4
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/compare_failures.py
@@ -0,0 +1,170 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A helper class for comparing the failures and crashes between layout test
+runs. The results from the last test run are stored in expected-failures.txt
+and expected-crashes.txt in the layout test results directory."""
+
+import errno
+import os
+
+import path_utils
+import test_failures
+import test_expectations
+
+
+def PrintFilesFromSet(filenames, header_text):
+ """A helper method to print a list of files to stdout.
+
+ Args:
+ filenames: a list of absolute filenames
+ header_text: a string to display before the list of filenames
+ """
+ if not len(filenames):
+ return
+
+ filenames = list(filenames)
+ filenames.sort()
+ print
+ print header_text, "(%d):" % len(filenames)
+ for filename in filenames:
+ print " %s" % path_utils.RelativeTestFilename(filename)
+
+
+class CompareFailures:
+ # A list of which TestFailure classes count as a failure vs a crash.
+ FAILURE_TYPES = (test_failures.FailureTextMismatch,
+ test_failures.FailureImageHashMismatch)
+ CRASH_TYPES = (test_failures.FailureCrash,)
+ HANG_TYPES = (test_failures.FailureTimeout,)
+ MISSING_TYPES = (test_failures.FailureMissingResult,
+ test_failures.FailureMissingImageHash)
+
+
+ def __init__(self, test_files, test_failures, expectations):
+ """Read the past layout test run's failures from disk.
+
+ Args:
+ test_files is a set of the filenames of all the test cases we ran
+ test_failures is a dictionary mapping the test filename to a list of
+ TestFailure objects if the test failed
+ expectations is a TestExpectations object representing the
+ current test status
+ """
+ self._test_files = test_files
+ self._test_failures = test_failures
+ self._expectations = expectations
+ self._CalculateRegressions()
+
+
+ def PrintRegressions(self):
+ """Print the regressions computed by _CalculateRegressions() to stdout. """
+
+ print "-" * 78
+
+ # Print unexpected passes by category.
+ passes = self._regressed_passes
+ PrintFilesFromSet(passes & self._expectations.GetFixableFailures(),
+ "Expected to fail, but passed")
+ PrintFilesFromSet(passes & self._expectations.GetFixableTimeouts(),
+ "Expected to timeout, but passed")
+ PrintFilesFromSet(passes & self._expectations.GetFixableCrashes(),
+ "Expected to crash, but passed")
+
+ PrintFilesFromSet(passes & self._expectations.GetIgnoredFailures(),
+ "Expected to fail (ignored), but passed")
+ PrintFilesFromSet(passes & self._expectations.GetIgnoredTimeouts(),
+ "Expected to timeout (ignored), but passed")
+
+ PrintFilesFromSet(passes & self._expectations.GetFixableDeferredFailures(),
+ "Expected to fail (deferred), but passed")
+ PrintFilesFromSet(passes & self._expectations.GetFixableDeferredTimeouts(),
+ "Expected to timeout (deferred), but passed")
+
+ # Print real regressions.
+ PrintFilesFromSet(self._regressed_failures,
+ "Regressions: Unexpected failures")
+ PrintFilesFromSet(self._regressed_hangs,
+ "Regressions: Unexpected timeouts")
+ PrintFilesFromSet(self._regressed_crashes,
+ "Regressions: Unexpected crashes")
+ PrintFilesFromSet(self._missing, "Missing expected results")
+
+
+ def _CalculateRegressions(self):
+ """Calculate regressions from this run through the layout tests."""
+ worklist = self._test_files.copy()
+
+ passes = set()
+ crashes = set()
+ hangs = set()
+ missing = set()
+ failures = set()
+
+ for test, failure_types in self._test_failures.iteritems():
+ # Although each test can have multiple test_failures, we only put them
+ # into one list (either the crash list or the failure list). We give
+ # priority to a crash/timeout over others, and to missing results over
+ # a text mismatch.
+ is_crash = [True for f in failure_types if type(f) in self.CRASH_TYPES]
+ is_hang = [True for f in failure_types if type(f) in self.HANG_TYPES]
+ is_missing = [True for f in failure_types
+ if type(f) in self.MISSING_TYPES]
+ is_failure = [True for f in failure_types
+ if type(f) in self.FAILURE_TYPES]
+ expectations = self._expectations.GetExpectations(test)
+ if is_crash:
+ if not test_expectations.CRASH in expectations: crashes.add(test)
+ elif is_hang:
+ if not test_expectations.TIMEOUT in expectations: hangs.add(test)
+ elif is_missing:
+ missing.add(test)
+ elif is_failure:
+ if not test_expectations.FAIL in expectations: failures.add(test)
+ worklist.remove(test)
+
+ for test in worklist:
+ # Check that all passing tests are expected to pass.
+ expectations = self._expectations.GetExpectations(test)
+ if not test_expectations.PASS in expectations: passes.add(test)
+
+ self._regressed_passes = passes
+ self._regressed_crashes = crashes
+ self._regressed_hangs = hangs
+ self._missing = missing
+ self._regressed_failures = failures
+
+
+ def GetRegressions(self):
+ """Returns a set of regressions from the test expectations. This is
+ used to determine which tests to list in results.html and the
+ right script exit code for the build bots. The list does not
+ include the unexpected passes."""
+ return (self._regressed_failures | self._regressed_hangs |
+ self._regressed_crashes | self._missing)
diff --git a/webkit/tools/layout_tests/layout_package/compare_failures_unittest.py b/webkit/tools/layout_tests/layout_package/compare_failures_unittest.py
new file mode 100644
index 0000000..a33b5fc
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/compare_failures_unittest.py
@@ -0,0 +1,305 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Unittests to make sure we generate and update the expected-*.txt files
+properly after running layout tests."""
+
+import os
+import shutil
+import tempfile
+import unittest
+
+import compare_failures
+import path_utils
+import test_failures
+
+class CompareFailuresUnittest(unittest.TestCase):
+ def setUp(self):
+ """Makes a temporary results directory and puts expected-failures.txt and
+ expected-crashes.txt into it."""
+ self._tempdir = tempfile.mkdtemp()
+ # copy over expected-*.txt files
+ testdatadir = self.GetTestDataDir()
+ filenames = ("expected-passing.txt", "expected-failures.txt",
+ "expected-crashes.txt")
+ for filename in filenames:
+ # copyfile doesn't copy file permissions so we can delete the files later
+ shutil.copyfile(os.path.join(testdatadir, filename),
+ os.path.join(self._tempdir, filename))
+
+ def tearDown(self):
+ """Remove temp directory."""
+ shutil.rmtree(self._tempdir)
+ self._tempdir = None
+
+ ###########################################################################
+ # Tests
+ def testGenerateNewBaseline(self):
+ """Test the generation of new expected-*.txt files when either they don't
+ exist or the user explicitly asks to make new files."""
+ failures = self.GetTestFailures()
+
+ # Test to make sure we generate baseline files if the file doesn't exist.
+ os.remove(os.path.join(self.GetTmpDir(), 'expected-passing.txt'))
+ os.remove(os.path.join(self.GetTmpDir(), 'expected-failures.txt'))
+ os.remove(os.path.join(self.GetTmpDir(), 'expected-crashes.txt'))
+
+ # Test force generation of new baseline files with a new failure and one
+ # less passing.
+ pass_file = os.path.join(path_utils.LayoutDataDir(), 'fast', 'pass1.html')
+ failures[pass_file] = [test_failures.FailureTextMismatch(None)]
+
+ cf = compare_failures.CompareFailures(self.GetTestFiles(), failures,
+ set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing-new-baseline.txt',
+ 'expected-failures-added.txt',
+ 'expected-crashes.txt')
+
+ def testPassingToFailure(self):
+ """When there's a new failure, we don't add it to the baseline."""
+ failures = self.GetTestFailures()
+
+ # Test case where we don't update new baseline. We have a new failure,
+ # but it shouldn't be added to the expected-failures.txt file.
+ pass_file = os.path.join(path_utils.LayoutDataDir(), 'fast', 'pass1.html')
+ failures[pass_file] = [test_failures.FailureTextMismatch(None)]
+ self.CheckNoChanges(failures)
+
+ # Same thing as before: pass -> crash
+ failures[pass_file] = [test_failures.FailureCrash()]
+ self.CheckNoChanges(failures)
+
+ def testFailureToCrash(self):
+ """When there's a new crash, we don't add it to the baseline or remove it
+ from the failure list."""
+ failures = self.GetTestFailures()
+
+ # Test case where we don't update new baseline. A failure moving to a
+ # crash shouldn't be added to the expected-crashes.txt file.
+ failure_file = os.path.join(path_utils.LayoutDataDir(),
+ 'fast', 'foo', 'fail1.html')
+ failures[failure_file] = [test_failures.FailureCrash()]
+ self.CheckNoChanges(failures)
+
+ def testFailureToPassing(self):
+ """This is better than before, so we should update the failure list."""
+ failures = self.GetTestFailures()
+
+ # Remove one of the failing test cases from the failures dictionary. This
+ # makes failure_file considered to be passing.
+ failure_file = os.path.join(path_utils.LayoutDataDir(),
+ 'fast', 'bar', 'fail2.html')
+ del failures[failure_file]
+
+ cf = compare_failures.CompareFailures(self.GetTestFiles(), failures,
+ set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing-new-passing2.txt',
+ 'expected-failures-new-passing.txt',
+ 'expected-crashes.txt')
+
+ def testCrashToPassing(self):
+ """This is better than before, so we update the crashes file."""
+ failures = self.GetTestFailures()
+
+ crash_file = os.path.join(path_utils.LayoutDataDir(),
+ 'fast', 'bar', 'betz', 'crash3.html')
+ del failures[crash_file]
+ cf = compare_failures.CompareFailures(self.GetTestFiles(), failures,
+ set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing-new-passing.txt',
+ 'expected-failures.txt',
+ 'expected-crashes-new-passing.txt')
+
+ def testCrashToFailure(self):
+ """This is better than before, so we should update both lists."""
+ failures = self.GetTestFailures()
+
+ crash_file = os.path.join(path_utils.LayoutDataDir(),
+ 'fast', 'bar', 'betz', 'crash3.html')
+ failures[crash_file] = [test_failures.FailureTextMismatch(None)]
+ cf = compare_failures.CompareFailures(self.GetTestFiles(), failures,
+ set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing.txt',
+ 'expected-failures-new-crash.txt',
+ 'expected-crashes-new-passing.txt')
+
+ def testNewTestPass(self):
+ """After a merge, we need to update new passing tests properly."""
+ files = self.GetTestFiles()
+ new_test_file = os.path.join(path_utils.LayoutDataDir(), "new-test.html")
+ files.add(new_test_file)
+ failures = self.GetTestFailures()
+
+ # New test file passing
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing-new-test.txt',
+ 'expected-failures.txt',
+ 'expected-crashes.txt')
+
+ def testNewTestFail(self):
+ """After a merge, we need to update new failing tests properly."""
+ files = self.GetTestFiles()
+ new_test_file = os.path.join(path_utils.LayoutDataDir(), "new-test.html")
+ files.add(new_test_file)
+ failures = self.GetTestFailures()
+
+ # New test file failing
+ failures[new_test_file] = [test_failures.FailureTextMismatch(None)]
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing.txt',
+ 'expected-failures-new-test.txt',
+ 'expected-crashes.txt')
+
+ def testNewTestCrash(self):
+ """After a merge, we need to update new crashing tests properly."""
+ files = self.GetTestFiles()
+ new_test_file = os.path.join(path_utils.LayoutDataDir(), "new-test.html")
+ files.add(new_test_file)
+ failures = self.GetTestFailures()
+
+ # New test file crashing
+ failures[new_test_file] = [test_failures.FailureCrash()]
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing.txt',
+ 'expected-failures.txt',
+ 'expected-crashes-new-test.txt')
+
+ def testHasNewFailures(self):
+ files = self.GetTestFiles()
+ failures = self.GetTestFailures()
+
+ # no changes, no new failures
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ self.failUnless(not cf.HasNewFailures())
+
+ # test goes from passing to failing
+ pass_file = os.path.join(path_utils.LayoutDataDir(), 'fast', 'pass1.html')
+ failures[pass_file] = [test_failures.FailureTextMismatch(None)]
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ self.failUnless(cf.HasNewFailures())
+
+ # Failing to passing
+ failures = self.GetTestFailures()
+ failure_file = os.path.join(path_utils.LayoutDataDir(),
+ 'fast', 'bar', 'fail2.html')
+ del failures[failure_file]
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ self.failUnless(not cf.HasNewFailures())
+
+ # A new test that fails, this doesn't count as a new failure.
+ new_test_file = os.path.join(path_utils.LayoutDataDir(), "new-test.html")
+ files.add(new_test_file)
+ failures = self.GetTestFailures()
+ failures[new_test_file] = [test_failures.FailureCrash()]
+ cf = compare_failures.CompareFailures(files, failures, set(), set(),
+ self.GetTmpDir(), False)
+ self.failUnless(not cf.HasNewFailures())
+
+
+ ###########################################################################
+ # Helper methods
+ def CheckOutputEqualsExpectedFile(self, output, expected):
+ """Compares a file in our output dir against a file from the testdata
+ directory."""
+ output = os.path.join(self.GetTmpDir(), output)
+ expected = os.path.join(self.GetTestDataDir(), expected)
+ self.failUnlessEqual(open(output).read(), open(expected).read())
+
+ def CheckOutputWithExpectedFiles(self, passing, failing, crashing):
+ """Compare all three output files against three provided expected
+ files."""
+ self.CheckOutputEqualsExpectedFile('expected-passing.txt', passing)
+ self.CheckOutputEqualsExpectedFile('expected-failures.txt', failing)
+ self.CheckOutputEqualsExpectedFile('expected-crashes.txt', crashing)
+
+ def CheckNoChanges(self, failures):
+ """Verify that none of the expected-*.txt files have changed."""
+ cf = compare_failures.CompareFailures(self.GetTestFiles(), failures,
+ set(), set(),
+ self.GetTmpDir(), False)
+ cf.UpdateFailuresOnDisk()
+ self.CheckOutputWithExpectedFiles('expected-passing.txt',
+ 'expected-failures.txt',
+ 'expected-crashes.txt')
+
+ def GetTestDataDir(self):
+ return os.path.abspath('testdata')
+
+ def GetTmpDir(self):
+ return self._tempdir
+
+ def GetTestFiles(self):
+ """Get a set of files that includes the expected crashes and failures
+ along with two passing tests."""
+ layout_dir = path_utils.LayoutDataDir()
+ files = [
+ 'fast\\pass1.html',
+ 'fast\\foo\\pass2.html',
+ 'fast\\foo\\crash1.html',
+ 'fast\\bar\\crash2.html',
+ 'fast\\bar\\betz\\crash3.html',
+ 'fast\\foo\\fail1.html',
+ 'fast\\bar\\fail2.html',
+ 'fast\\bar\\betz\\fail3.html',
+ ]
+
+ return set([os.path.join(layout_dir, f) for f in files])
+
+ def GetTestFailures(self):
+ """Get a dictionary representing the crashes and failures in the
+ expected-*.txt files."""
+ failures = {}
+ for filename in self.GetTestFiles():
+ if filename.find('crash') != -1:
+ failures[filename] = [test_failures.FailureCrash()]
+ elif filename.find('fail') != -1:
+ failures[filename] = [test_failures.FailureTextMismatch(None)]
+
+ return failures
+
+if '__main__' == __name__:
+ unittest.main()
diff --git a/webkit/tools/layout_tests/layout_package/http_server.py b/webkit/tools/layout_tests/layout_package/http_server.py
new file mode 100644
index 0000000..aac1cca
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/http_server.py
@@ -0,0 +1,219 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A class to help start/stop the lighttpd server used by layout tests."""
+
+
+import logging
+import optparse
+import os
+import subprocess
+import sys
+import time
+import urllib
+
+import google.path_utils
+
+# This will be a native path to the directory this file resides in.
+# It can either be relative or absolute depending how it's executed.
+THISDIR = os.path.dirname(os.path.abspath(__file__))
+
+def PathFromBase(*pathies):
+ return google.path_utils.FindUpward(THISDIR, *pathies)
+
+class HttpdNotStarted(Exception):
+ pass
+
+class Lighttpd:
+ # Webkit tests
+ _webkit_tests = PathFromBase('webkit', 'data', 'layout_tests',
+ 'LayoutTests', 'http', 'tests')
+ # New tests for Chrome
+ _pending_tests = PathFromBase('webkit', 'data', 'layout_tests',
+ 'pending', 'http', 'tests')
+ # Path where we can access all of the tests
+ _all_tests = PathFromBase('webkit', 'data', 'layout_tests')
+ # Self generated certificate for SSL server (for client cert get
+ # <base-path>\chrome\test\data\ssl\certs\root_ca_cert.crt)
+ _pem_file = PathFromBase('tools', 'python', 'google', 'httpd_config',
+ 'httpd2.pem')
+ VIRTUALCONFIG = [
+ # Three mappings (one with SSL enabled) for LayoutTests http tests
+ {'port': 8000, 'docroot': _webkit_tests},
+ {'port': 8080, 'docroot': _webkit_tests},
+ {'port': 8443, 'docroot': _webkit_tests, 'sslcert': _pem_file},
+ # Three similar mappings (one with SSL enabled) for pending http tests
+ {'port': 9000, 'docroot': _pending_tests},
+ {'port': 9080, 'docroot': _pending_tests},
+ {'port': 9443, 'docroot': _pending_tests, 'sslcert': _pem_file},
+ # One mapping where we can get to everything
+ {'port': 8081, 'docroot': _all_tests}
+ ]
+
+ def __init__(self, output_dir, background=False):
+ """Args:
+ output_dir: the absolute path to the layout test result directory
+ """
+ self._output_dir = output_dir
+ self._process = None
+
+ def IsRunning(self):
+ return self._process != None
+
+ def Start(self):
+ if self.IsRunning():
+ raise 'Lighttpd already running'
+
+ base_conf_file = os.path.join(THISDIR, 'lighttpd.conf')
+ out_conf_file = os.path.join(self._output_dir, 'lighttpd.conf')
+ access_log = os.path.join(self._output_dir, 'access.log.txt')
+ error_log = os.path.join(self._output_dir, 'error.log.txt')
+
+ # Write out the config
+ f = file(base_conf_file, 'rb')
+ base_conf = f.read()
+ f.close()
+
+ f = file(out_conf_file, 'wb')
+ f.write(base_conf)
+
+ # Write out our cgi handlers. Run perl through env so that it processes
+ # the #! line and runs perl with the proper command line arguments.
+ # Emulate apache's mod_asis with a cat cgi handler.
+ f.write(('cgi.assign = ( ".cgi" => "/usr/bin/env",\n'
+ ' ".pl" => "/usr/bin/env",\n'
+ ' ".asis" => "/usr/bin/cat",\n'
+ ' ".php" => "%s" )\n\n') %
+ PathFromBase('third_party', 'lighttpd', 'php5', 'php-cgi.exe'))
+
+ # Setup log files
+ f.write(('server.errorlog = "%s"\n'
+ 'accesslog.filename = "%s"\n\n') % (error_log, access_log))
+
+ # dump out of virtual host config at the bottom.
+ for mapping in self.VIRTUALCONFIG:
+ ssl_setup = ''
+ if 'sslcert' in mapping:
+ ssl_setup = (' ssl.engine = "enable"\n'
+ ' ssl.pemfile = "%s"\n' % mapping['sslcert'])
+
+ f.write(('$SERVER["socket"] == "127.0.0.1:%d" {\n'
+ ' server.document-root = "%s"\n' +
+ ssl_setup +
+ '}\n\n') % (mapping['port'], mapping['docroot']))
+ f.close()
+
+ start_cmd = [ PathFromBase('third_party', 'lighttpd', 'LightTPD.exe'),
+ # Newly written config file
+ '-f', PathFromBase(self._output_dir, 'lighttpd.conf'),
+ # Where it can find it's module dynamic libraries
+ '-m', PathFromBase('third_party', 'lighttpd', 'lib'),
+ # Don't background
+ '-D' ]
+
+ # Put the cygwin directory first in the path to find cygwin1.dll
+ env = os.environ
+ env['PATH'] = '%s;%s' % (
+ PathFromBase('third_party', 'cygwin', 'bin'), env['PATH'])
+
+ logging.info('Starting http server')
+ self._process = subprocess.Popen(start_cmd, env=env)
+
+ # Ensure that the server is running on all the desired ports.
+ for mapping in self.VIRTUALCONFIG:
+ url = 'http%s://127.0.0.1:%d/' % ('sslcert' in mapping and 's' or '',
+ mapping['port'])
+ if not self._UrlIsAlive(url):
+ raise HttpdNotStarted('Failed to start httpd on port %s' % str(port))
+
+ # Our process terminated already
+ if self._process.returncode != None:
+ raise HttpdNotStarted('Failed to start httpd.')
+
+ def _UrlIsAlive(self, url):
+ """Checks to see if we get an http response from |url|.
+ We poll the url 5 times with a 1 second delay. If we don't
+ get a reply in that time, we give up and assume the httpd
+ didn't start properly.
+
+ Args:
+ url: The URL to check.
+ Return:
+ True if the url is alive.
+ """
+ wait_time = 5
+ while wait_time > 0:
+ try:
+ response = urllib.urlopen(url)
+ # Server is up and responding.
+ return True
+ except IOError:
+ pass
+ wait_time -= 1
+ # Wait a second and try again.
+ time.sleep(1)
+
+ return False
+
+ # TODO(deanm): Find a nicer way to shutdown cleanly. Our log files are
+ # probably not being flushed, etc... why doesn't our python have os.kill ?
+ def Stop(self, force=False):
+ if not force and not self.IsRunning():
+ return
+
+ logging.info('Shutting down http server')
+
+ subprocess.Popen(('taskkill.exe', '/f', '/im', 'LightTPD.exe'),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).wait()
+
+ if self._process:
+ self._process.wait()
+ self._process = None
+
+ # Wait a bit to make sure the ports are free'd up
+ time.sleep(2)
+
+
+if '__main__' == __name__:
+ # Provide some command line params for starting/stopping the http server
+ # manually.
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('-k', '--server', help='Server action (start|stop)')
+ options, args = option_parser.parse_args()
+
+ if not options.server:
+ print "Usage: %s --server {start|stop} [--apache2]" % sys.argv[0]
+ else:
+ httpd = Lighttpd('c:/cygwin/tmp')
+ if 'start' == options.server:
+ httpd.Start()
+ else:
+ httpd.Stop(force=True)
diff --git a/webkit/tools/layout_tests/layout_package/lighttpd.conf b/webkit/tools/layout_tests/layout_package/lighttpd.conf
new file mode 100644
index 0000000..2b53d78
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/lighttpd.conf
@@ -0,0 +1,74 @@
+server.tag = "LightTPD/1.4.19 (Win32)"
+server.modules = ( "mod_accesslog",
+ "mod_cgi",
+ "mod_rewrite" )
+
+# default document root required
+server.document-root = "."
+
+# files to check for if .../ is requested
+index-file.names = ( "index.php", "index.pl", "index.cgi",
+ "index.html", "index.htm", "default.htm" )
+# mimetype mapping
+mimetype.assign = (
+ ".gif" => "image/gif",
+ ".jpg" => "image/jpeg",
+ ".jpeg" => "image/jpeg",
+ ".png" => "image/png",
+ ".css" => "text/css",
+ ".html" => "text/html",
+ ".htm" => "text/html",
+ ".xhtml" => "application/xhtml+xml",
+ ".js" => "text/javascript",
+ ".log" => "text/plain",
+ ".conf" => "text/plain",
+ ".text" => "text/plain",
+ ".txt" => "text/plain",
+ ".dtd" => "text/xml",
+ ".xml" => "text/xml",
+ )
+
+# Use the "Content-Type" extended attribute to obtain mime type if possible
+mimetype.use-xattr = "enable"
+
+##
+# which extensions should not be handle via static-file transfer
+#
+# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
+static-file.exclude-extensions = ( ".php", ".pl", ".cgi" )
+
+server.bind = "localhost"
+server.port = 8001
+
+## virtual directory listings
+dir-listing.activate = "enable"
+#dir-listing.encoding = "iso-8859-2"
+#dir-listing.external-css = "style/oldstyle.css"
+
+## enable debugging
+#debug.log-request-header = "enable"
+#debug.log-response-header = "enable"
+#debug.log-request-handling = "enable"
+#debug.log-file-not-found = "enable"
+
+#### SSL engine
+#ssl.engine = "enable"
+#ssl.pemfile = "server.pem"
+
+# Rewrite rule for utf-8 path test (LayoutTests/http/tests/uri/utf8-path.html)
+# See the apache rewrite rule at LayoutTests/http/tests/uri/intercept/.htaccess
+url.rewrite-once = ( "^/uri/intercept/(.*)" => "/uri/resources/print-uri.php" )
+
+# LayoutTests/http/tests/xmlhttprequest/response-encoding.html uses an htaccess
+# to override charset for reply2.txt, reply2.xml, and reply4.txt.
+$HTTP["url"] =~ "^/xmlhttprequest/resources/reply2.(txt|xml)" {
+ mimetype.assign = (
+ ".txt" => "text/plain; charset=windows-1251",
+ ".xml" => "text/xml; charset=windows-1251"
+ )
+}
+$HTTP["url"] =~ "^/xmlhttprequest/resources/reply4.txt" {
+ mimetype.assign = ( ".txt" => "text/plain; charset=koi8-r" )
+}
+
+# Autogenerated test-specific config follows.
diff --git a/webkit/tools/layout_tests/layout_package/path_utils.py b/webkit/tools/layout_tests/layout_package/path_utils.py
new file mode 100644
index 0000000..292d298
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/path_utils.py
@@ -0,0 +1,190 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Some utility methods for getting paths used by run_webkit_tests.py.
+"""
+
+import errno
+import os
+import subprocess
+import sys
+
+import google.path_utils
+
+
+class PathNotFound(Exception): pass
+
+# Save some paths here so we don't keep re-evaling.
+_webkit_root = None
+_layout_data_dir = None
+_expected_results_dir = None
+_platform_results_dirs = {}
+
+# TODO this should probably be moved into path_utils as ToUnixPath().
+def WinPathToUnix(path):
+ """Convert a windows path to use unix-style path separators (a/b/c)."""
+ return path.replace('\\', '/')
+
+def WebKitRoot():
+ """Returns the full path to the directory containing webkit.sln. Raises
+ PathNotFound if we're unable to find webkit.sln."""
+ global _webkit_root
+ if _webkit_root:
+ return _webkit_root
+ webkit_sln_path = google.path_utils.FindUpward(google.path_utils.ScriptDir(),
+ 'webkit.sln')
+ _webkit_root = os.path.dirname(webkit_sln_path)
+ return _webkit_root
+
+def LayoutDataDir():
+ """Gets the full path to the tests directory. Raises PathNotFound if
+ we're unable to find it."""
+ global _layout_data_dir
+ if _layout_data_dir:
+ return _layout_data_dir
+ _layout_data_dir = google.path_utils.FindUpward(WebKitRoot(), 'webkit',
+ 'data', 'layout_tests')
+ return _layout_data_dir
+
+def ExpectedResultsDir():
+ """Gets the full path to the custom_results directory. Raises
+ PathNotFound if we're unable to find it."""
+ global _expected_results_dir
+ if _expected_results_dir:
+ return _expected_results_dir
+ _expected_results_dir = google.path_utils.FindUpward(WebKitRoot(), 'webkit',
+ 'data',
+ 'layout_test_results')
+ return _expected_results_dir
+
+def CustomExpectedResultsDir(custom_id):
+ """Gets the full path to the directory in which custom expected results for
+ this app and build type are located.
+
+ Args:
+ custom_id: a string specifying the particular set of results to use (e.g.,
+ 'v8' or 'kjs')
+ """
+ return os.path.join(ExpectedResultsDir(), custom_id)
+
+def PlatformResultsDir(name):
+ """Gets the full path to a platform-specific results directory. Raises
+ PathNotFound if we're unable to find it."""
+ global _platform_results_dirs
+ if _platform_results_dirs.get(name):
+ return _platform_results_dirs[name]
+ _platform_results_dirs[name] = google.path_utils.FindUpward(WebKitRoot(),
+ 'webkit', 'data', 'layout_tests', 'LayoutTests', 'platform', name)
+ return _platform_results_dirs[name]
+
+def ExpectedFilename(filename, suffix, custom_result_id):
+ """Given a test name, returns an absolute filename to the most specific
+ applicable file of expected results.
+
+ Args:
+ filename: absolute filename to test file
+ suffix: file suffix of the expected results, including dot; e.g. '.txt'
+ or '.png'. This should not be None, but may be an empty string.
+ custom_result_id: Tells us where to look for custom results. Currently
+ this is either kjs or v8.
+
+ Return:
+ If a file named <testname>-expected<suffix> exists in the subdirectory
+ of the ExpectedResultsDir() specified by this platform's identifier,
+ return its absolute path. Otherwise, return a path to a
+ <testname>-expected<suffix> file under the MacExpectedResultsDir() or
+ <testname>-expected<suffix> file under the MacLeopardExpectedResultsDir()
+ or (if not found there) in the same directory as the test file (even if
+ that default file does not exist).
+ """
+ testname = os.path.splitext(RelativeTestFilename(filename))[0]
+ results_filename = testname + '-expected' + suffix
+ results_dirs = [
+ CustomExpectedResultsDir(custom_result_id),
+ CustomExpectedResultsDir('common'),
+ LayoutDataDir()
+ ]
+
+ for results_dir in results_dirs:
+ platform_file = os.path.join(results_dir, results_filename)
+ if os.path.exists(platform_file):
+ return platform_file
+
+ # for 'base' tests, we need to look for mac-specific results
+ if testname.startswith('LayoutTests'):
+ layout_test_results_dirs = [
+ PlatformResultsDir('mac'),
+ PlatformResultsDir('mac-leopard'),
+ PlatformResultsDir('mac-tiger')
+ ]
+ rel_testname = testname[len('LayoutTests') + 1:]
+ rel_filename = rel_testname + '-expected' + suffix
+ for results_dir in layout_test_results_dirs:
+ platform_file = os.path.join(results_dir, rel_filename)
+ if os.path.exists(platform_file):
+ return platform_file
+
+ # Failed to find the results anywhere, return default path anyway
+ return os.path.join(results_dirs[0], results_filename)
+
+def TestShellBinary():
+ """Returns the name of the test_shell executable."""
+ return 'test_shell.exe'
+
+def TestShellBinaryPath(target):
+ """Gets the full path to the test_shell binary for the target build
+ configuration. Raises PathNotFound if the file doesn't exist"""
+ full_path = os.path.join(WebKitRoot(), target, TestShellBinary())
+ if not os.path.exists(full_path):
+ # try chrome's output directory in case test_shell was built by chrome.sln
+ full_path = google.path_utils.FindUpward(WebKitRoot(), 'chrome', target,
+ TestShellBinary())
+ if not os.path.exists(full_path):
+ raise PathNotFound('unable to find test_shell at %s' % full_path)
+ return full_path
+
+def RelativeTestFilename(filename):
+ """Provide the filename of the test relative to the layout data
+ directory as a unix style path (a/b/c)."""
+ return WinPathToUnix(filename[len(LayoutDataDir()) + 1:])
+
+# Map platform specific path utility functions. We do this as a convenience
+# so importing path_utils will get all path related functions even if they are
+# platform specific.
+def GetAbsolutePath(path):
+ # Avoid circular import by delaying it.
+ import layout_package.platform_utils
+ platform_util = layout_package.platform_utils.PlatformUtility(WebKitRoot())
+ return platform_util.GetAbsolutePath(path)
+
+def FilenameToUri(path):
+ # Avoid circular import by delaying it.
+ import layout_package.platform_utils
+ platform_util = layout_package.platform_utils.PlatformUtility(WebKitRoot())
+ return platform_util.FilenameToUri(path)
diff --git a/webkit/tools/layout_tests/layout_package/platform_utils.py b/webkit/tools/layout_tests/layout_package/platform_utils.py
new file mode 100644
index 0000000..1fcb182
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/platform_utils.py
@@ -0,0 +1,46 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Platform-specific utilities and pseudo-constants
+
+Any functions whose implementations or values differ from one platform to
+another should be defined in their respective platform_utils_<platform>.py
+modules. The appropriate one of those will be imported into this module to
+provide callers with a common, platform-independent interface.
+"""
+
+import sys
+
+# We may not support the version of Python that a user has installed (Cygwin
+# especially has had problems), but we'll allow the platform utils to be
+# included in any case so we don't get an import error.
+if sys.platform in ('cygwin', 'win32'):
+ from platform_utils_win import *
+
diff --git a/webkit/tools/layout_tests/layout_package/platform_utils_win.py b/webkit/tools/layout_tests/layout_package/platform_utils_win.py
new file mode 100644
index 0000000..542533a
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/platform_utils_win.py
@@ -0,0 +1,168 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Platform specific utility methods. This file contains methods that are
+specific to running the layout tests on windows.
+
+This file constitutes a complete wrapper for google.platform_utils_win,
+implementing or mapping all needed functions from there. Layout-test scripts
+should be able to import only this file (via platform_utils.py), with no need
+to fall back to the base functions.
+"""
+
+import os
+import re
+import subprocess
+
+import google.httpd_utils
+import google.path_utils
+import google.platform_utils_win
+
+import layout_package.path_utils
+
+
+class PlatformUtility(google.platform_utils_win.PlatformUtility):
+ """Overrides base PlatformUtility methods as needed for layout tests."""
+
+ LAYOUTTEST_HTTP_DIR = "LayoutTests/http/tests/"
+ PENDING_HTTP_DIR = "pending/http/tests/"
+
+ def FilenameToUri(self, full_path):
+ relative_path = layout_package.path_utils.RelativeTestFilename(full_path)
+ port = None
+ use_ssl = False
+
+ # LayoutTests/http/tests/ run off port 8000 and ssl/ off 8443
+ if relative_path.startswith(self.LAYOUTTEST_HTTP_DIR):
+ relative_path = relative_path[len(self.LAYOUTTEST_HTTP_DIR):]
+ port = 8000
+ # pending/http/tests/ run off port 9000 and ssl/ off 9443
+ elif relative_path.startswith(self.PENDING_HTTP_DIR):
+ relative_path = relative_path[len(self.PENDING_HTTP_DIR):]
+ port = 9000
+ # chrome/http/tests run off of port 8081 with the full path
+ elif relative_path.find("/http/") >= 0:
+ print relative_path
+ port = 8081
+
+ # We want to run off of the http server
+ if port:
+ if relative_path.startswith("ssl/"):
+ port += 443
+ use_ssl = True
+ return google.platform_utils_win.PlatformUtility.FilenameToUri(self,
+ relative_path,
+ use_http=True,
+ use_ssl=use_ssl,
+ port=port)
+
+ # Run off file://
+ return google.platform_utils_win.PlatformUtility.FilenameToUri(
+ self, full_path)
+
+ def KillAllTestShells(self):
+ """Kills all instances of the test_shell binary currently running."""
+ subprocess.Popen(('taskkill.exe', '/f', '/im',
+ layout_package.path_utils.TestShellBinary()),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).wait()
+
+ def _GetVirtualHostConfig(self, document_root, port, ssl=False):
+ """Returns a <VirtualHost> directive block for an httpd.conf file. It will
+ listen to 127.0.0.1 on each of the given port.
+ """
+ cygwin_document_root = google.platform_utils_win.GetCygwinPath(
+ document_root)
+
+ return '\n'.join(('<VirtualHost 127.0.0.1:%s>' % port,
+ 'DocumentRoot %s' % cygwin_document_root,
+ ssl and 'SSLEngine On' or '',
+ '</VirtualHost>', ''))
+
+ def GetStartHttpdCommand(self, output_dir, apache2=False):
+ """Prepares the config file and output directory to start an httpd server.
+ Returns a list of strings containing the server's command line+args.
+
+ Creates the test output directory and generates an httpd.conf (or
+ httpd2.conf for Apache 2 if apache2 is True) file in it that contains
+ the necessary <VirtualHost> directives for running all the http tests.
+
+ WebKit http tests expect the DocumentRoot to be in LayoutTests/http/tests/,
+ but that prevents us from running http tests in chrome/ or pending/. So we
+ run two virtual hosts, one on ports 8000 and 8080 for WebKit, and one on
+ port 8081 with a much broader DocumentRoot for everything else. (Note that
+ WebKit http tests that have been modified and are temporarily in pending/
+ will still fail, if they expect the DocumentRoot to be located as described
+ above.)
+
+ Args:
+ output_dir: the path to the test output directory. It will be created.
+ apache2: boolean if true will cause this function to return start
+ command for Apache 2.x instead of Apache 1.3.x
+ """
+ layout_dir = google.platform_utils_win.GetCygwinPath(
+ layout_package.path_utils.LayoutDataDir())
+ main_document_root = os.path.join(layout_dir, "LayoutTests",
+ "http", "tests")
+ pending_document_root = os.path.join(layout_dir, "pending",
+ "http", "tests")
+ chrome_document_root = layout_dir
+ apache_config_dir = google.httpd_utils.ApacheConfigDir(self._base_dir)
+ mime_types_path = os.path.join(apache_config_dir, "mime.types")
+
+ conf_file_name = "httpd.conf"
+ if apache2:
+ conf_file_name = "httpd2.conf"
+ # Make the test output directory and place the generated httpd.conf in it.
+ orig_httpd_conf_path = os.path.join(apache_config_dir, conf_file_name)
+
+ httpd_conf_path = os.path.join(output_dir, conf_file_name)
+ google.path_utils.MaybeMakeDirectory(output_dir)
+ httpd_conf = open(orig_httpd_conf_path).read()
+ httpd_conf = (httpd_conf +
+ self._GetVirtualHostConfig(main_document_root, 8000) +
+ self._GetVirtualHostConfig(main_document_root, 8080) +
+ self._GetVirtualHostConfig(pending_document_root, 9000) +
+ self._GetVirtualHostConfig(pending_document_root, 9080) +
+ self._GetVirtualHostConfig(chrome_document_root, 8081))
+ if apache2:
+ httpd_conf += self._GetVirtualHostConfig(main_document_root, 8443,
+ ssl=True)
+ httpd_conf += self._GetVirtualHostConfig(pending_document_root, 9443,
+ ssl=True)
+ f = open(httpd_conf_path, 'wb')
+ f.write(httpd_conf)
+ f.close()
+
+ return google.platform_utils_win.PlatformUtility.GetStartHttpdCommand(
+ self,
+ output_dir,
+ httpd_conf_path,
+ mime_types_path,
+ apache2=apache2)
diff --git a/webkit/tools/layout_tests/layout_package/test_expectations.py b/webkit/tools/layout_tests/layout_package/test_expectations.py
new file mode 100644
index 0000000..7ea0fb1
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/test_expectations.py
@@ -0,0 +1,368 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A helper class for reading in and dealing with tests expectations
+for layout tests. """
+
+import os
+import re
+import path_utils
+import compare_failures
+
+
+# Test expectation constants.
+PASS = 0
+FAIL = 1
+TIMEOUT = 2
+CRASH = 3
+
+
+class TestExpectations:
+ FIXABLE = "tests_fixable.txt"
+ IGNORED = "tests_ignored.txt"
+
+ def __init__(self, tests, directory, build_type):
+ """Reads the test expectations files from the given directory."""
+ self._tests = tests
+ self._directory = directory
+ self._build_type = build_type
+ self._ReadFiles()
+ self._ValidateLists()
+
+ def GetFixable(self):
+ return (self._fixable.GetTests() -
+ self._fixable.GetNonSkippedDeferred() -
+ self._fixable.GetSkippedDeferred())
+
+ def GetFixableSkipped(self):
+ return self._fixable.GetSkipped()
+
+ def GetFixableSkippedDeferred(self):
+ return self._fixable.GetSkippedDeferred()
+
+ def GetFixableFailures(self):
+ return (self._fixable.GetTestsExpectedTo(FAIL) -
+ self._fixable.GetTestsExpectedTo(TIMEOUT) -
+ self._fixable.GetTestsExpectedTo(CRASH) -
+ self._fixable.GetNonSkippedDeferred())
+
+ def GetFixableTimeouts(self):
+ return (self._fixable.GetTestsExpectedTo(TIMEOUT) -
+ self._fixable.GetTestsExpectedTo(CRASH) -
+ self._fixable.GetNonSkippedDeferred())
+
+ def GetFixableCrashes(self):
+ return self._fixable.GetTestsExpectedTo(CRASH)
+
+ def GetFixableDeferred(self):
+ return self._fixable.GetNonSkippedDeferred()
+
+ def GetFixableDeferredFailures(self):
+ return (self._fixable.GetNonSkippedDeferred() &
+ self._fixable.GetTestsExpectedTo(FAIL))
+
+ def GetFixableDeferredTimeouts(self):
+ return (self._fixable.GetNonSkippedDeferred() &
+ self._fixable.GetTestsExpectedTo(TIMEOUT))
+
+ def GetIgnored(self):
+ return self._ignored.GetTests()
+
+ def GetIgnoredSkipped(self):
+ return self._ignored.GetSkipped()
+
+ def GetIgnoredFailures(self):
+ return (self._ignored.GetTestsExpectedTo(FAIL) -
+ self._ignored.GetTestsExpectedTo(TIMEOUT))
+
+ def GetIgnoredTimeouts(self):
+ return self._ignored.GetTestsExpectedTo(TIMEOUT)
+
+ def GetExpectations(self, test):
+ if self._fixable.Contains(test): return self._fixable.GetExpectations(test)
+ if self._ignored.Contains(test): return self._ignored.GetExpectations(test)
+ # If the test file is not listed in any of the expectations lists
+ # we expect it to pass (and nothing else).
+ return set([PASS])
+
+ def IsFixable(self, test):
+ return (self._fixable.Contains(test) and
+ test not in self._fixable.GetNonSkippedDeferred())
+
+ def IsDeferred(self, test):
+ return (self._fixable.Contains(test) and
+ test in self._fixable.GetNonSkippedDeferred())
+
+ def IsIgnored(self, test):
+ return self._ignored.Contains(test)
+
+ def _ReadFiles(self):
+ self._fixable = self._GetExpectationsFile(self.FIXABLE)
+ self._ignored = self._GetExpectationsFile(self.IGNORED)
+ skipped = self.GetFixableSkipped() | self.GetIgnoredSkipped()
+ self._fixable.PruneSkipped(skipped)
+ self._ignored.PruneSkipped(skipped)
+
+ def _GetExpectationsFile(self, filename):
+ """Read the expectation files for the given filename and return a single
+ expectations file with the merged results.
+ """
+
+ path = os.path.join(self._directory, filename)
+ return TestExpectationsFile(path, self._tests, self._build_type)
+
+ def _ValidateLists(self):
+ # Make sure there's no overlap between the tests in the two files.
+ overlap = self._fixable.GetTests() & self._ignored.GetTests()
+ message = "Files contained in both " + self.FIXABLE + " and " + self.IGNORED
+ compare_failures.PrintFilesFromSet(overlap, message)
+ assert(len(overlap) == 0)
+ # Make sure there are no ignored tests expected to crash.
+ assert(len(self._ignored.GetTestsExpectedTo(CRASH)) == 0)
+
+
+def StripComments(line):
+ """Strips comments from a line and return None if the line is empty
+ or else the contents of line with leading and trailing spaces removed
+ and all other whitespace collapsed"""
+
+ commentIndex = line.find('//')
+ if commentIndex is -1:
+ commentIndex = len(line)
+
+ line = re.sub(r'\s+', ' ', line[:commentIndex].strip())
+ if line == '': return None
+ else: return line
+
+
+class TestExpectationsFile:
+ """Test expectation files consist of lines with specifications of what
+ to expect from layout test cases. The test cases can be directories
+ in which case the expectations apply to all test cases in that
+ directory and any subdirectory. The format of the file is along the
+ lines of:
+
+ KJS # LayoutTests/fast/js/fixme.js = FAIL
+ V8 # LayoutTests/fast/js/flaky.js = FAIL | PASS
+ V8 | KJS # LayoutTests/fast/js/crash.js = CRASH | TIMEOUT | FAIL | PASS
+ ...
+
+ In case you want to skip tests completely, add a SKIP:
+ V8 | KJS # SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT | PASS
+
+ If you want the test to not count in our statistics for the current release,
+ add a DEFER:
+ V8 | KJS # DEFER : LayoutTests/fast/js/no-good.js = TIMEOUT | PASS
+
+ And you can skip + defer a test:
+ V8 | KJS # DEFER | SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT | PASS
+
+ You can also have different expecations for V8 and KJS
+ V8 # LayoutTests/fast/js/no-good.js = TIMEOUT | PASS
+ KJS # DEFER | SKIP : LayoutTests/fast/js/no-good.js = FAIL
+
+ A test can be included twice, but not via the same path. If a test is included
+ twice, then the more precise path wins.
+ """
+
+ EXPECTATIONS = { 'pass': PASS,
+ 'fail': FAIL,
+ 'timeout': TIMEOUT,
+ 'crash': CRASH }
+
+ BUILD_TYPES = [ 'kjs', 'v8' ]
+
+
+ def __init__(self, path, full_test_list, build_type):
+ """path is the path to the expectation file. An error is thrown if a test
+ is listed more than once for a given build_type.
+ full_test_list is the list of all tests to be run pending processing of the
+ expections for those tests.
+ build_type is used to filter out tests that only have expectations for
+ a different build_type.
+
+ """
+
+ self._full_test_list = full_test_list
+ self._skipped = set()
+ self._skipped_deferred = set()
+ self._non_skipped_deferred = set()
+ self._expectations = {}
+ self._test_list_paths = {}
+ self._tests = {}
+ for expectation in self.EXPECTATIONS.itervalues():
+ self._tests[expectation] = set()
+ self._Read(path, build_type)
+
+ def GetSkipped(self):
+ return self._skipped
+
+ def GetNonSkippedDeferred(self):
+ return self._non_skipped_deferred
+
+ def GetSkippedDeferred(self):
+ return self._skipped_deferred
+
+ def GetExpectations(self, test):
+ return self._expectations[test]
+
+ def GetTests(self):
+ return set(self._expectations.keys())
+
+ def GetTestsExpectedTo(self, expectation):
+ return self._tests[expectation]
+
+ def Contains(self, test):
+ return test in self._expectations
+
+ def PruneSkipped(self, skipped):
+ for test in skipped:
+ if not test in self._expectations: continue
+ for expectation in self._expectations[test]:
+ self._tests[expectation].remove(test)
+ del self._expectations[test]
+
+ def _Read(self, path, build_type):
+ """For each test in an expectations file, generate the expectations for it.
+
+ """
+
+ lineno = 0
+ for line in open(path):
+ lineno += 1
+ line = StripComments(line)
+ if not line: continue
+
+ parts = line.split('#')
+ if len(parts) is not 2:
+ self._ReportSyntaxError(path, lineno, "Test must have build types")
+
+ if build_type not in self._GetOptionsList(parts[0]): continue
+
+ parts = parts[1].split(':')
+
+ if len(parts) is 2:
+ test_and_expectations = parts[1]
+ skip_defer_options = self._GetOptionsList(parts[0])
+ is_skipped = 'skip' in skip_defer_options
+ is_deferred = 'defer' in skip_defer_options
+ else:
+ test_and_expectations = parts[0]
+ is_skipped = False
+ is_deferred = False
+
+ tests_and_expecation_parts = test_and_expectations.split('=')
+ if (len(tests_and_expecation_parts) is not 2):
+ self._ReportSyntaxError(path, lineno, "Test is missing expectations")
+
+ test_list_path = tests_and_expecation_parts[0].strip()
+ tests = self._ExpandTests(test_list_path)
+
+ if is_skipped:
+ self._AddSkippedTests(tests, is_deferred)
+ else:
+ try:
+ self._AddTests(tests,
+ self._ParseExpectations(tests_and_expecation_parts[1]),
+ test_list_path,
+ is_deferred)
+ except SyntaxError, err:
+ self._ReportSyntaxError(path, lineno, str(err))
+
+ def _GetOptionsList(self, listString):
+ return [part.strip().lower() for part in listString.split('|')]
+
+ def _ParseExpectations(self, string):
+ result = set()
+ for part in self._GetOptionsList(string):
+ if not part in self.EXPECTATIONS:
+ raise SyntaxError('Unsupported expectation: ' + part)
+ expectation = self.EXPECTATIONS[part]
+ result.add(expectation)
+ return result
+
+ def _ExpandTests(self, test_list_path):
+ # Convert the test specification to an absolute, normalized
+ # path and make sure directories end with the OS path separator.
+ path = os.path.join(path_utils.LayoutDataDir(), test_list_path)
+ path = os.path.normpath(path)
+ if os.path.isdir(path): path = os.path.join(path, '')
+ # This is kind of slow - O(n*m) - since this is called for all
+ # entries in the test lists. It has not been a performance
+ # issue so far. Maybe we should re-measure the time spent reading
+ # in the test lists?
+ result = []
+ for test in self._full_test_list:
+ if test.startswith(path): result.append(test)
+ return result
+
+ def _AddTests(self, tests, expectations, test_list_path, is_deferred):
+ # Do not add tests that we expect only to pass to the lists.
+ # This makes it easier to account for tests that we expect to
+ # consistently pass, because they'll never be represented in
+ # any of the lists.
+ if len(expectations) == 1 and PASS in expectations: return
+ # Traverse all tests and add them with the given expectations.
+
+ for test in tests:
+ if test in self._test_list_paths:
+ prev_base_path = self._test_list_paths[test]
+ if (prev_base_path == os.path.normpath(test_list_path)):
+ raise SyntaxError('Already seen expectations for path ' + test)
+ if prev_base_path.startswith(test_list_path):
+ # already seen a more precise path
+ continue
+
+ # Remove prexisiting expectations for this test.
+ if test in self._test_list_paths:
+ if test in self._non_skipped_deferred:
+ self._non_skipped_deferred.remove(test)
+
+ for expectation in self.EXPECTATIONS.itervalues():
+ if test in self._tests[expectation]:
+ self._tests[expectation].remove(test)
+
+ # Now add the new expectations.
+ self._expectations[test] = expectations
+ self._test_list_paths[test] = os.path.normpath(test_list_path)
+
+ if is_deferred:
+ self._non_skipped_deferred.add(test)
+
+ for expectation in expectations:
+ self._tests[expectation].add(test)
+
+ def _AddSkippedTests(self, tests, is_deferred):
+ for test in tests:
+ self._skipped.add(test)
+ if is_deferred:
+ self._skipped_deferred.add(test)
+
+ def _ReportSyntaxError(self, path, lineno, message):
+ raise SyntaxError(path + ':' + str(lineno) + ': ' + message)
diff --git a/webkit/tools/layout_tests/layout_package/test_failures.py b/webkit/tools/layout_tests/layout_package/test_failures.py
new file mode 100644
index 0000000..685c591
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/test_failures.py
@@ -0,0 +1,215 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Classes for failures that occur during tests."""
+
+import path_utils
+
+class FailureSort(object):
+ """A repository for failure sort orders and tool to facilitate sorting."""
+
+ # Each failure class should have an entry in this dictionary. Sort order 1
+ # will be sorted first in the list. Failures with the same numeric sort
+ # order will be sorted alphabetically by Message().
+ SORT_ORDERS = {
+ 'FailureTextMismatch': 1,
+ 'FailureSimplifiedTextMismatch': 2,
+ 'FailureImageHashMismatch': 3,
+ 'FailureTimeout': 4,
+ 'FailureCrash': 5,
+ 'FailureMissingImageHash': 6,
+ 'FailureMissingImage': 7,
+ 'FailureMissingResult': 8,
+ }
+
+ @staticmethod
+ def SortOrder(failure_type):
+ """Returns a tuple of the class's numeric sort order and its message."""
+ order = FailureSort.SORT_ORDERS.get(failure_type.__name__, -1)
+ return (order, failure_type.Message())
+
+
+class TestFailure(object):
+ """Abstract base class that defines the failure interface."""
+ @staticmethod
+ def Message():
+ """Returns a string describing the failure in more detail."""
+ raise NotImplemented
+
+ def ResultHtmlOutput(self, filename):
+ """Returns an HTML string to be included on the results.html page."""
+ raise NotImplemented
+
+ def ShouldKillTestShell(self):
+ """Returns True if we should kill the test shell before the next test."""
+ return False
+
+
+class FailureWithType(TestFailure):
+ """Base class that produces standard HTML output based on the test type.
+
+ Subclasses may commonly choose to override the ResultHtmlOutput, but still
+ use the standard OutputLinks.
+ """
+ def __init__(self, test_type):
+ TestFailure.__init__(self)
+ self._test_type = test_type
+
+ # Filename suffixes used by ResultHtmlOutput.
+ OUT_FILENAMES = []
+
+ def OutputLinks(self, filename, out_names):
+ """Returns a string holding all applicable output file links.
+
+ Args:
+ filename: the test filename, used to construct the result file names
+ out_names: list of filename suffixes for the files. If three or more
+ suffixes are in the list, they should be [actual, expected, diff].
+ Two suffixes should be [actual, expected], and a single item is the
+ [actual] filename suffix. If out_names is empty, returns the empty
+ string.
+ """
+ links = ['']
+ uris = [self._test_type.RelativeOutputFilename(filename, fn)
+ for fn in out_names]
+ if len(uris) > 1:
+ links.append("<a href='%s'>expected</a>" % uris[1])
+ if len(uris) > 0:
+ links.append("<a href='%s'>actual</a>" % uris[0])
+ if len(uris) > 2:
+ links.append("<a href='%s'>diff</a>" % uris[2])
+ return ' '.join(links)
+
+ def ResultHtmlOutput(self, filename):
+ return self.Message() + self.OutputLinks(filename, self.OUT_FILENAMES)
+
+
+class FailureTimeout(TestFailure):
+ """Test timed out. We also want to restart the test shell if this
+ happens."""
+ @staticmethod
+ def Message():
+ return "Test timed out"
+
+ def ResultHtmlOutput(self, filename):
+ return "<strong>%s</strong>" % self.Message()
+
+ def ShouldKillTestShell(self):
+ return True
+
+
+class FailureCrash(TestFailure):
+ """Test shell crashed."""
+ @staticmethod
+ def Message():
+ return "Test shell crashed"
+
+ def ResultHtmlOutput(self, filename):
+ # TODO(tc): create a link to the minidump file
+ return "<strong>%s</strong>" % self.Message()
+
+ def ShouldKillTestShell(self):
+ return True
+
+
+class FailureMissingResult(FailureWithType):
+ """Expected result was missing."""
+ OUT_FILENAMES = ["-actual-win.txt"]
+
+ @staticmethod
+ def Message():
+ return "No expected results found"
+
+ def ResultHtmlOutput(self, filename):
+ return ("<strong>%s</strong>" % self.Message() +
+ self.OutputLinks(filename, self.OUT_FILENAMES))
+
+
+class FailureTextMismatch(FailureWithType):
+ """Text diff output failed."""
+ # Filename suffixes used by ResultHtmlOutput.
+ OUT_FILENAMES = ["-actual-win.txt", "-expected.txt", "-diff-win.txt"]
+
+ @staticmethod
+ def Message():
+ return "Text diff mismatch"
+
+
+class FailureSimplifiedTextMismatch(FailureTextMismatch):
+ """Simplified text diff output failed.
+
+ The results.html output format is basically the same as regular diff
+ failures (links to expected, actual and diff text files) so we share code
+ with the FailureTextMismatch class.
+ """
+
+ OUT_FILENAMES = ["-simp-actual-win.txt", "-simp-expected.txt",
+ "-simp-diff-win.txt"]
+
+ @staticmethod
+ def Message():
+ return "Simplified text diff mismatch"
+
+
+class FailureMissingImageHash(FailureWithType):
+ """Actual result hash was missing."""
+ # Chrome doesn't know to display a .checksum file as text, so don't bother
+ # putting in a link to the actual result.
+ OUT_FILENAMES = []
+
+ @staticmethod
+ def Message():
+ return "No expected image hash found"
+
+ def ResultHtmlOutput(self, filename):
+ return "<strong>%s</strong>" % self.Message()
+
+
+class FailureMissingImage(FailureWithType):
+ """Actual result image was missing."""
+ OUT_FILENAMES = ["-actual-win.png"]
+
+ @staticmethod
+ def Message():
+ return "No expected image found"
+
+ def ResultHtmlOutput(self, filename):
+ return ("<strong>%s</strong>" % self.Message() +
+ self.OutputLinks(filename, self.OUT_FILENAMES))
+
+
+class FailureImageHashMismatch(FailureWithType):
+ """Image hashes didn't match."""
+ OUT_FILENAMES = ["-actual-win.png", "-expected.png"]
+
+ @staticmethod
+ def Message():
+ # We call this a simple image mismatch to avoid confusion, since we link
+ # to the PNGs rather than the checksums.
+ return "Image mismatch"
diff --git a/webkit/tools/layout_tests/layout_package/test_shell_thread.py b/webkit/tools/layout_tests/layout_package/test_shell_thread.py
new file mode 100644
index 0000000..e28a30e
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/test_shell_thread.py
@@ -0,0 +1,301 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A Thread object for running the test shell and processing URLs from a
+shared queue.
+
+Each thread runs a separate instance of the test_shell binary and validates
+the output. When there are no more URLs to process in the shared queue, the
+thread exits.
+"""
+
+import copy
+import logging
+import os
+import Queue
+import subprocess
+import thread
+import threading
+
+import path_utils
+import platform_utils
+import test_failures
+
+# The per-test timeout in milliseconds, if no --time-out-ms option was given to
+# run_webkit_tests. This should correspond to the default timeout in
+# test_shell.exe.
+DEFAULT_TEST_TIMEOUT_MS = 10 * 1000
+
+def ProcessOutput(proc, filename, test_uri, test_types, test_args):
+ """Receives the output from a test_shell process, subjects it to a number
+ of tests, and returns a list of failure types the test produced.
+
+ Args:
+ proc: an active test_shell process
+ filename: path of the test file being run
+ test_types: list of test types to subject the output to
+ test_args: arguments to be passed to each test
+
+ Returns: a list of failure objects for the test being processed
+ """
+ outlines = []
+ failures = []
+ crash_or_timeout = False
+
+ # Some test args, such as the image hash, may be added or changed on a
+ # test-by-test basis.
+ local_test_args = copy.copy(test_args)
+
+ line = proc.stdout.readline()
+ while line.rstrip() != "#EOF":
+ # Make sure we haven't crashed.
+ if line == '' and proc.poll() is not None:
+ failures.append(test_failures.FailureCrash())
+
+ # This is hex code 0xc000001d, which is used for abrupt termination.
+ # This happens if we hit ctrl+c from the prompt and we happen to
+ # be waiting on the test_shell.
+ if -1073741510 == proc.returncode:
+ raise KeyboardInterrupt
+ crash_or_timeout = True
+ break
+
+ # Don't include #URL lines in our output
+ if line.startswith("#URL:"):
+ url = line.rstrip()[5:]
+ if url != test_uri:
+ logging.fatal("Test got out of sync:\n|%s|\n|%s|" %
+ (url, test_uri))
+ raise AssertionError("test out of sync")
+ elif line.startswith("#MD5:"):
+ local_test_args.hash = line.rstrip()[5:]
+ elif line.startswith("#TEST_TIMED_OUT"):
+ # Test timed out, but we still need to read until #EOF.
+ crash_or_timeout = True
+ failures.append(test_failures.FailureTimeout())
+ else:
+ outlines.append(line)
+ line = proc.stdout.readline()
+
+ # Check the output and save the results.
+ for test_type in test_types:
+ new_failures = test_type.CompareOutput(filename, proc,
+ ''.join(outlines),
+ local_test_args)
+ # Don't add any more failures if we already have a crash or timeout, so
+ # we don't double-report those tests.
+ if not crash_or_timeout:
+ failures.extend(new_failures)
+
+ return failures
+
+
+def StartTestShell(binary, args):
+ """Returns the process for a new test_shell started in layout-tests mode."""
+ cmd = [binary, '--layout-tests'] + args
+ return subprocess.Popen(cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+
+class SingleTestThread(threading.Thread):
+ """Thread wrapper for running a single test file."""
+ def __init__(self, test_shell_binary, shell_args, test_uri, filename,
+ test_types, test_args):
+ """
+ Args:
+ test_uri: full file:// or http:// URI of the test file to be run
+ filename: absolute local path to the test file
+ See TestShellThread for documentation of the remaining arguments.
+ """
+
+ threading.Thread.__init__(self)
+ self._binary = test_shell_binary
+ self._shell_args = shell_args
+ self._test_uri = test_uri
+ self._filename = filename
+ self._test_types = test_types
+ self._test_args = test_args
+ self._single_test_failures = []
+
+ def run(self):
+ proc = StartTestShell(self._binary, self._shell_args + [self._test_uri])
+ self._single_test_failures = ProcessOutput(proc,
+ self._filename,
+ self._test_uri,
+ self._test_types,
+ self._test_args)
+
+ def GetFailures(self):
+ return self._single_test_failures
+
+
+class TestShellThread(threading.Thread):
+
+ def __init__(self, filename_queue, test_shell_binary, test_types,
+ test_args, shell_args, options):
+ """Initialize all the local state for this test shell thread.
+
+ Args:
+ filename_queue: A thread safe Queue class that contains tuples of
+ (filename, uri) pairs.
+ test_shell_binary: The path to test_shell.exe
+ test_types: A list of TestType objects to run the test output against.
+ test_args: A TestArguments object to pass to each TestType.
+ shell_args: Any extra arguments to be passed to test_shell.exe.
+ options: A property dictionary as produced by optparse. The command-line
+ options should match those expected by run_webkit_tests; they
+ are typically passed via the run_webkit_tests.TestRunner class.
+ """
+ threading.Thread.__init__(self)
+ self._filename_queue = filename_queue
+ self._test_shell_binary = test_shell_binary
+ self._test_types = test_types
+ self._test_args = test_args
+ self._test_shell_proc = None
+ self._shell_args = shell_args
+ self._options = options
+ self._failures = {}
+
+ if self._options.run_singly:
+ # When we're running one test per test_shell process, we can enforce
+ # a hard timeout. test_shell uses a default of 10 seconds if no
+ # time-out-ms is given, and the test_shell watchdog uses 2.5x the
+ # test_shell's value. We want to be larger than that.
+ if not self._options.time_out_ms:
+ self._options.time_out_ms = DEFAULT_TEST_TIMEOUT_MS
+ self._time_out_sec = int(self._options.time_out_ms) * 3.0 / 1000.0
+ logging.info("Setting Python per-test timeout to %s ms (%s sec)" %
+ (1000 * self._time_out_sec, self._time_out_sec))
+
+
+ def GetFailures(self):
+ """Returns a dictionary mapping test filename to a list of
+ TestFailures."""
+ return self._failures
+
+ def run(self):
+ """Main work entry point of the thread. Basically we pull urls from the
+ filename queue and run the tests until we run out of urls."""
+ while True:
+ try:
+ filename, test_uri = self._filename_queue.get_nowait()
+ except Queue.Empty:
+ self._KillTestShell()
+ logging.debug("queue empty, quitting test shell thread")
+ return
+
+ # we have a url, run tests
+ if self._options.run_singly:
+ failures = self._RunTestSingly(filename, test_uri)
+ else:
+ failures = self._RunTest(filename, test_uri)
+ if failures:
+ # Check and kill test shell if we need to
+ if len([1 for f in failures if f.ShouldKillTestShell()]):
+ self._KillTestShell()
+ # print the error message(s)
+ error_str = '\n'.join([' ' + f.Message() for f in failures])
+ logging.error("%s failed:\n%s" %
+ (path_utils.RelativeTestFilename(filename), error_str))
+ # Group the errors for reporting
+ self._failures[filename] = failures
+ else:
+ logging.debug(path_utils.RelativeTestFilename(filename) + " passed")
+
+
+ def _RunTestSingly(self, filename, test_uri):
+ """Run a test in a separate thread, enforcing a hard time limit.
+
+ Since we can only detect the termination of a thread, not any internal
+ state or progress, we can only run per-test timeouts when running test
+ files singly.
+ """
+ worker = SingleTestThread(self._test_shell_binary,
+ self._shell_args,
+ test_uri,
+ filename,
+ self._test_types,
+ self._test_args)
+ worker.start()
+ worker.join(self._time_out_sec)
+ if worker.isAlive():
+ # If join() returned with the thread still running, the test_shell.exe is
+ # completely hung and there's nothing more we can do with it. We have
+ # to kill all the test_shells to free it up. If we're running more than
+ # one test_shell thread, we'll end up killing the other test_shells too,
+ # introducing spurious crashes. We accept that tradeoff in order to
+ # avoid losing the rest of this thread's results.
+ logging.error('Test thread hung: killing all test_shells')
+ # PlatformUtility() wants a base_dir, but it doesn't matter here.
+ platform_util = platform_utils.PlatformUtility('')
+ platform_util.KillAllTestShells()
+
+ return worker.GetFailures()
+
+
+ def _RunTest(self, filename, test_uri):
+ """Run a single test file using a shared test_shell process.
+
+ Args:
+ filename: The absolute filename of the test
+ test_uri: The URI version of the filename
+
+ Return:
+ A list of TestFailure objects describing the error.
+ """
+ self._EnsureTestShellIsRunning()
+
+ # Ok, load the test URL...
+ self._test_shell_proc.stdin.write(test_uri + "\n")
+ self._test_shell_proc.stdin.flush()
+
+ # ...and read the response
+ return ProcessOutput(self._test_shell_proc, filename, test_uri,
+ self._test_types, self._test_args)
+
+
+ def _EnsureTestShellIsRunning(self):
+ """Start the shared test shell, if it's not running. Not for use when
+ running tests singly, since those each start a separate test shell in
+ their own thread.
+ """
+ if (not self._test_shell_proc or
+ self._test_shell_proc.poll() is not None):
+ self._test_shell_proc = StartTestShell(self._test_shell_binary,
+ self._shell_args)
+
+ def _KillTestShell(self):
+ """Kill the test shell process if it's running."""
+ if self._test_shell_proc:
+ self._test_shell_proc.stdin.close()
+ self._test_shell_proc.stdout.close()
+ self._test_shell_proc.stderr.close()
+ self._test_shell_proc = None
diff --git a/webkit/tools/layout_tests/layout_package/test_types_unittest.py b/webkit/tools/layout_tests/layout_package/test_types_unittest.py
new file mode 100644
index 0000000..dca5feb
--- /dev/null
+++ b/webkit/tools/layout_tests/layout_package/test_types_unittest.py
@@ -0,0 +1,71 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Unittests that verify that the various test_types (e.g., simplified_diff)
+are working."""
+
+import difflib
+import os
+import unittest
+
+from test_types import simplified_text_diff
+
+class SimplifiedDiffUnittest(unittest.TestCase):
+ def testSimplifiedDiff(self):
+ """Compares actual output with expected output for some test cases. The
+ simplified diff of these test cases should be the same."""
+ test_names = [
+ 'null-offset-parent',
+ 'textAreaLineHeight',
+ 'form-element-geometry',
+ ]
+ differ = simplified_text_diff.SimplifiedTextDiff(None)
+ for prefix in test_names:
+ output_filename = os.path.join(self.GetTestDataDir(),
+ prefix + "-actual-win.txt")
+ expected_filename = os.path.join(self.GetTestDataDir(),
+ prefix + "-expected.txt")
+ output = differ._SimplifyText(open(output_filename).read())
+ expected = differ._SimplifyText(open(expected_filename).read())
+
+ if output != expected:
+ lst = difflib.unified_diff(expected.splitlines(True),
+ output.splitlines(True),
+ 'expected',
+ 'actual')
+ for line in lst:
+ print line.rstrip()
+ self.failUnlessEqual(output, expected)
+
+ def GetTestDataDir(self):
+ return os.path.join(os.path.abspath('testdata'), 'difftests')
+
+if '__main__' == __name__:
+ unittest.main()
diff --git a/webkit/tools/layout_tests/run_webkit_tests.py b/webkit/tools/layout_tests/run_webkit_tests.py
new file mode 100644
index 0000000..e2af53f
--- /dev/null
+++ b/webkit/tools/layout_tests/run_webkit_tests.py
@@ -0,0 +1,653 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Run layout tests using the test_shell.
+
+This is a port of the existing webkit test script run-webkit-tests.
+
+The TestRunner class runs a series of tests (TestType interface) against a set
+of test files. If a test file fails a TestType, it returns a list TestFailure
+objects to the TestRunner. The TestRunner then aggregates the TestFailures to
+create a final report.
+
+This script reads several files, if they exist in the test_lists subdirectory
+next to this script itself. Each should contain a list of paths to individual
+tests or entire subdirectories of tests, relative to the outermost test
+directory. Entire lines starting with '//' (comments) will be ignored.
+
+For details of the files' contents and purposes, see test_lists/README.
+"""
+
+import glob
+import logging
+import optparse
+import os
+import Queue
+import shutil
+import subprocess
+import sys
+import time
+
+import google.path_utils
+
+from layout_package import compare_failures
+from layout_package import test_expectations
+from layout_package import http_server
+from layout_package import path_utils
+from layout_package import test_failures
+from layout_package import test_shell_thread
+from test_types import image_diff
+from test_types import test_type_base
+from test_types import text_diff
+from test_types import simplified_text_diff
+
+
+# The test list files are found in this subdirectory, which must be a sibling
+# to this script itself.
+TEST_FILE_DIR = 'test_lists'
+
+
+class TestRunner:
+ """A class for managing running a series of tests on a series of test
+ files."""
+
+ # When collecting test cases, we include any file with these extensions.
+ _supported_file_extensions = set(['.html', '.shtml', '.xml', '.xhtml', '.pl',
+ '.php', '.svg'])
+ # When collecting test cases, skip these directories
+ _skipped_directories = set(['.svn', '_svn', 'resources'])
+
+ HTTP_SUBDIR = os.sep.join(['', 'http', ''])
+
+ def __init__(self, options, paths):
+ """Collect a list of files to test.
+
+ Args:
+ options: a dictionary of command line options
+ paths: a list of paths to crawl looking for test files
+ """
+ self._options = options
+
+ self._http_server = http_server.Lighttpd(options.results_directory)
+ # a list of TestType objects
+ self._test_types = []
+ # a set of test files
+ self._test_files = set()
+
+ if options.nosvg:
+ TestRunner._supported_file_extensions.remove('.svg')
+ TestRunner._skipped_directories.add('svg')
+
+ self._GatherTestFiles(paths)
+
+ def __del__(self):
+ sys.stdout.flush()
+ sys.stderr.flush()
+ # Stop the http server.
+ self._http_server.Stop()
+
+ def _GatherTestFiles(self, paths):
+ """Generate a set of test files and place them in self._test_files, with
+ appropriate subsets in self._ignored_failures, self._fixable_failures,
+ and self._fixable_crashes.
+
+ Args:
+ paths: a list of command line paths relative to the webkit/tests
+ directory. glob patterns are ok.
+ """
+ paths_to_walk = set()
+ for path in paths:
+ # If there's an * in the name, assume it's a glob pattern.
+ path = os.path.join(path_utils.LayoutDataDir(), path)
+ if path.find('*') > -1:
+ filenames = glob.glob(path)
+ paths_to_walk.update(filenames)
+ else:
+ paths_to_walk.add(path)
+
+ # Now walk all the paths passed in on the command line and get filenames
+ for path in paths_to_walk:
+ if os.path.isfile(path) and self._HasSupportedExtension(path):
+ self._test_files.add(os.path.normpath(path))
+ continue
+
+ for root, dirs, files in os.walk(path):
+ # don't walk skipped directories and sub directories
+ if os.path.basename(root) in TestRunner._skipped_directories:
+ del dirs[:]
+ continue
+
+ for filename in files:
+ if self._HasSupportedExtension(filename):
+ filename = os.path.join(root, filename)
+ filename = os.path.normpath(filename)
+ self._test_files.add(filename)
+
+ # Filter out http tests if we're not running them.
+ if self._options.nohttp:
+ for path in list(self._test_files):
+ if path.find(self.HTTP_SUBDIR) >= 0:
+ self._test_files.remove(path)
+
+ # Filter and sort out files from the skipped, ignored, and fixable file
+ # lists.
+ saved_test_files = set()
+ if len(self._test_files) == 1:
+ # If there's only one test file, we don't want to skip it, but we do want
+ # to sort it. So we save it to add back to the list later.
+ saved_test_files = self._test_files
+
+ file_dir = os.path.join(os.path.dirname(sys.argv[0]), TEST_FILE_DIR)
+ file_dir = path_utils.GetAbsolutePath(file_dir)
+
+ expectations = test_expectations.TestExpectations(self._test_files,
+ file_dir,
+ self._options.build_type)
+
+ # Remove skipped - both fixable and ignored - files from the
+ # top-level list of files to test.
+ skipped = expectations.GetFixableSkipped() | expectations.GetIgnoredSkipped()
+ self._test_files -= skipped
+
+ # If there was only one test file, run the test even if it was skipped.
+ if len(saved_test_files):
+ self._test_files = saved_test_files
+
+ logging.info('Run: %d tests' % len(self._test_files))
+ logging.info('Skipped: %d tests' % len(skipped))
+ logging.info('Skipped tests do not appear in any of the below numbers\n')
+ logging.info('Deferred: %d tests' % len(expectations.GetFixableDeferred()))
+ logging.info('Expected passes: %d tests' %
+ len(self._test_files -
+ expectations.GetFixable() -
+ expectations.GetIgnored()))
+ logging.info(('Expected failures: %d fixable, %d ignored '
+ 'and %d deferred tests') %
+ (len(expectations.GetFixableFailures()),
+ len(expectations.GetIgnoredFailures()),
+ len(expectations.GetFixableDeferredFailures())))
+ logging.info(('Expected timeouts: %d fixable, %d ignored '
+ 'and %d deferred tests') %
+ (len(expectations.GetFixableTimeouts()),
+ len(expectations.GetIgnoredTimeouts()),
+ len(expectations.GetFixableDeferredTimeouts())))
+ logging.info('Expected crashes: %d fixable tests' %
+ len(expectations.GetFixableCrashes()))
+
+ # Store the expectations in this object to allow it to be used to
+ # track regressions and print results.
+ self._expectations = expectations
+
+ def _HasSupportedExtension(self, filename):
+ """Return true if filename is one of the file extensions we want to run a
+ test on."""
+ extension = os.path.splitext(filename)[1]
+ return extension in TestRunner._supported_file_extensions
+
+ def AddTestType(self, test_type):
+ """Add a TestType to the TestRunner."""
+ self._test_types.append(test_type)
+
+ # We sort the tests so that tests using the http server will run first. We
+ # are seeing some flakiness, maybe related to apache getting swapped out,
+ # slow, or stuck after not serving requests for a while.
+ def TestFilesSort(self, x, y):
+ """Sort with http tests always first."""
+ x_is_http = x.find(self.HTTP_SUBDIR) >= 0
+ y_is_http = y.find(self.HTTP_SUBDIR) >= 0
+ if x_is_http != y_is_http:
+ return cmp(y_is_http, x_is_http)
+ return cmp(x, y)
+
+ def Run(self):
+ """Run all our tests on all our test files.
+
+ For each test file, we run each test type. If there are any failures, we
+ collect them for reporting.
+
+ Return:
+ We return nonzero if there are regressions compared to the last run.
+ """
+ if not self._test_files:
+ return 0
+ start_time = time.time()
+ logging.info("Starting tests")
+
+ # Create the output directory if it doesn't already exist.
+ google.path_utils.MaybeMakeDirectory(self._options.results_directory)
+
+ test_files = list(self._test_files)
+ test_files.sort(self.TestFilesSort)
+ # Create the thread safe queue of (test filenames, test URIs) tuples. Each
+ # TestShellThread pulls values from this queue.
+ filename_queue = Queue.Queue()
+ for test_file in test_files:
+ filename_queue.put((test_file, path_utils.FilenameToUri(test_file)))
+
+ # If we have http tests, the first one will be an http test.
+ if test_files and test_files[0].find(self.HTTP_SUBDIR) >= 0:
+ self._http_server.Start()
+
+ # Instantiate TestShellThreads and start them.
+ threads = []
+ test_shell_binary = path_utils.TestShellBinaryPath(self._options.target)
+ for i in xrange(int(self._options.num_test_shells)):
+ shell_args = []
+ test_args = test_type_base.TestArguments()
+ if self._options.pixel_tests:
+ png_path = os.path.join(self._options.results_directory,
+ "png_result%s.png" % i)
+ shell_args.append("--pixel-tests=" + png_path)
+ test_args.png_path = png_path
+
+ if self._options.new_baseline:
+ test_args.new_baseline = self._options.new_baseline
+ if not self._options.pixel_tests:
+ test_args.text_baseline = True
+
+ # Create separate TestTypes instances for each thread.
+ test_types = []
+ for t in self._test_types:
+ test_types.append(t(self._options.build_type,
+ self._options.results_directory))
+
+ if self._options.startup_dialog:
+ shell_args.append('--testshell-startup-dialog')
+
+ # larger timeout if page heap is enabled.
+ if self._options.time_out_ms:
+ shell_args.append('--time-out-ms=' + self._options.time_out_ms)
+
+ thread = test_shell_thread.TestShellThread(filename_queue,
+ test_shell_binary,
+ test_types,
+ test_args,
+ shell_args,
+ self._options)
+ thread.start()
+ threads.append(thread)
+
+ # Wait for the threads to finish and collect test failures.
+ test_failures = {}
+ for thread in threads:
+ thread.join()
+ test_failures.update(thread.GetFailures())
+
+ print
+ end_time = time.time()
+ logging.info("%f total testing time" % (end_time - start_time))
+
+ # Tests are done running. Compare failures with expected failures.
+ regressions = self._CompareFailures(test_failures)
+
+ # Write summaries to stdout.
+ self._PrintResults(test_failures)
+
+ # Write the summary to disk (results.html) and maybe open the test_shell
+ # to this file.
+ wrote_results = self._WriteResultsHtmlFile(test_failures, regressions)
+ if not self._options.noshow_results and wrote_results:
+ self._ShowResultsHtmlFile()
+
+ sys.stdout.flush()
+ sys.stderr.flush()
+ return len(regressions)
+
+ def _PrintResults(self, test_failures):
+ """Print a short summary to stdout about how many tests passed.
+
+ Args:
+ test_failures is a dictionary mapping the test filename to a list of
+ TestFailure objects if the test failed
+ """
+
+ failure_counts = {}
+ deferred_counts = {}
+ fixable_counts = {}
+ non_ignored_counts = {}
+ fixable_failures = set()
+ deferred_failures = set()
+ non_ignored_failures = set()
+
+ # Aggregate failures in a dictionary (TestFailure -> frequency),
+ # with known (fixable and ignored) failures separated out.
+ def AddFailure(dictionary, key):
+ if key in dictionary:
+ dictionary[key] += 1
+ else:
+ dictionary[key] = 1
+
+ for test, failures in test_failures.iteritems():
+ for failure in failures:
+ AddFailure(failure_counts, failure.__class__)
+ if self._expectations.IsFixable(test):
+ AddFailure(fixable_counts, failure.__class__)
+ fixable_failures.add(test)
+ if self._expectations.IsDeferred(test):
+ AddFailure(deferred_counts, failure.__class__)
+ deferred_failures.add(test)
+ if (not self._expectations.IsIgnored(test) and
+ not self._expectations.IsDeferred(test)):
+ AddFailure(non_ignored_counts, failure.__class__)
+ non_ignored_failures.add(test)
+
+ # Print summaries.
+ print "-" * 78
+
+ # Print breakdown of tests we need to fix and want to pass.
+ # Include skipped fixable tests in the statistics.
+ skipped = (self._expectations.GetFixableSkipped() -
+ self._expectations.GetFixableSkippedDeferred())
+
+ self._PrintResultSummary("=> Tests to be fixed for the current release",
+ self._expectations.GetFixable(),
+ fixable_failures,
+ fixable_counts,
+ skipped)
+
+ self._PrintResultSummary("=> Tests we want to pass for the current release",
+ (self._test_files -
+ self._expectations.GetIgnored() -
+ self._expectations.GetFixableDeferred()),
+ non_ignored_failures,
+ non_ignored_counts,
+ skipped)
+
+ self._PrintResultSummary("=> Tests to be fixed for a future release",
+ self._expectations.GetFixableDeferred(),
+ deferred_failures,
+ deferred_counts,
+ self._expectations.GetFixableSkippedDeferred())
+
+ # Print breakdown of all tests including all skipped tests.
+ skipped |= self._expectations.GetIgnoredSkipped()
+ self._PrintResultSummary("=> All tests",
+ self._test_files,
+ test_failures,
+ failure_counts,
+ skipped)
+ print
+
+ def _PrintResultSummary(self, heading, all, failed, failure_counts, skipped):
+ """Print a summary block of results for a particular category of test.
+
+ Args:
+ heading: text to print before the block, followed by the total count
+ all: list of all tests in this category
+ failed: list of failing tests in this category
+ failure_counts: dictionary of (TestFailure -> frequency)
+ """
+ total = len(all | skipped)
+ print "\n%s (%d):" % (heading, total)
+ skip_count = len(skipped)
+ pass_count = total - skip_count - len(failed)
+ self._PrintResultLine(pass_count, total, "Passed")
+ self._PrintResultLine(skip_count, total, "Skipped")
+ # Sort the failure counts and print them one by one.
+ sorted_keys = sorted(failure_counts.keys(),
+ key=test_failures.FailureSort.SortOrder)
+ for failure in sorted_keys:
+ self._PrintResultLine(failure_counts[failure], total, failure.Message())
+
+ def _PrintResultLine(self, count, total, message):
+ if count == 0: return
+ print ("%(count)d test case%(plural)s (%(percent).1f%%) %(message)s" %
+ { 'count' : count,
+ 'plural' : ('s', '')[count == 1],
+ 'percent' : float(count) * 100 / total,
+ 'message' : message })
+
+ def _CompareFailures(self, test_failures):
+ """Determine how the test failures from this test run differ from the
+ previous test run and print results to stdout and a file.
+
+ Args:
+ test_failures is a dictionary mapping the test filename to a list of
+ TestFailure objects if the test failed
+
+ Return:
+ A set of regressions (unexpected failures, hangs, or crashes)
+ """
+ cf = compare_failures.CompareFailures(self._test_files,
+ test_failures,
+ self._expectations)
+
+ if not self._options.nocompare_failures: cf.PrintRegressions()
+ return cf.GetRegressions()
+
+ def _WriteResultsHtmlFile(self, test_failures, regressions):
+ """Write results.html which is a summary of tests that failed.
+
+ Args:
+ test_failures: a dictionary mapping the test filename to a list of
+ TestFailure objects if the test failed
+ regressions: a set of test filenames that regressed
+
+ Returns:
+ True if any results were written (since expected failures may be omitted)
+ """
+ # test failures
+ if self._options.full_results_html:
+ test_files = test_failures.keys()
+ else:
+ test_files = list(regressions)
+ if not len(test_files):
+ return False
+
+ out_filename = os.path.join(self._options.results_directory,
+ "results.html")
+ out_file = open(out_filename, 'w')
+ # header
+ if self._options.full_results_html:
+ h2 = "Test Failures"
+ else:
+ h2 = "Unexpected Test Failures"
+ out_file.write("<html><head><title>Layout Test Results (%(time)s)</title>"
+ "</head><body><h2>%(h2)s (%(time)s)</h2>\n"
+ % {'h2': h2, 'time': time.asctime()})
+
+ test_files.sort()
+ for test_file in test_files:
+ if test_file in test_failures: failures = test_failures[test_file]
+ else: failures = [] # unexpected passes
+ out_file.write("<p><a href='%s'>%s</a><br />\n"
+ % (path_utils.FilenameToUri(test_file),
+ path_utils.RelativeTestFilename(test_file)))
+ for failure in failures:
+ out_file.write("&nbsp;&nbsp;%s<br/>"
+ % failure.ResultHtmlOutput(
+ path_utils.RelativeTestFilename(test_file)))
+ out_file.write("</p>\n")
+
+ # footer
+ out_file.write("</body></html>\n")
+ return True
+
+ def _ShowResultsHtmlFile(self):
+ """Launches the test shell open to the results.html page."""
+ results_filename = os.path.join(self._options.results_directory,
+ "results.html")
+ subprocess.Popen([path_utils.TestShellBinaryPath(self._options.target),
+ path_utils.FilenameToUri(results_filename)])
+
+
+def ReadTestFiles(files):
+ tests = []
+ for file in files:
+ for line in open(file):
+ line = test_expectations.StripComments(line)
+ if line: tests.append(line)
+ return tests
+
+
+def main(options, args):
+ """Run the tests. Will call sys.exit when complete.
+
+ Args:
+ options: a dictionary of command line options
+ args: a list of sub directories or files to test
+ """
+ # Set up our logging format.
+ log_level = logging.INFO
+ if options.verbose:
+ log_level = logging.DEBUG
+ logging.basicConfig(level=log_level,
+ format='%(asctime)s %(filename)s:%(lineno)-3d'
+ ' %(levelname)s %(message)s',
+ datefmt='%y%m%d %H:%M:%S')
+
+ if not options.target:
+ if options.debug:
+ options.target = "Debug"
+ else:
+ options.target = "Release"
+
+ if (options.pixel_tests and
+ options.build_type != 'v8' and
+ not options.new_baseline):
+ logging.warn('Pixel tests disabled: no expected results for %s builds' %
+ options.build_type)
+ options.pixel_tests = False
+
+ if options.results_directory.startswith("/"):
+ # Assume it's an absolute path and normalize
+ options.results_directory = path_utils.GetAbsolutePath(
+ options.results_directory)
+ else:
+ # If it's a relative path, make the output directory relative to Debug or
+ # Release.
+ basedir = path_utils.WebKitRoot()
+ basedir = os.path.join(basedir, options.target)
+
+ options.results_directory = path_utils.GetAbsolutePath(
+ os.path.join(basedir, options.results_directory))
+
+ logging.info("Using expected results from %s" %
+ path_utils.CustomExpectedResultsDir(options.build_type))
+ logging.info("Placing test results in %s" % options.results_directory)
+ logging.info("Using %s build at %s" % (options.target,
+ path_utils.TestShellBinaryPath(options.target)))
+ if options.pixel_tests:
+ logging.info("Running pixel tests")
+
+ if 'cygwin' == sys.platform:
+ logging.warn("#" * 40)
+ logging.warn("# UNEXPECTED PYTHON VERSION")
+ logging.warn("# This script should be run using the version of python")
+ logging.warn("# in third_party/python_24/")
+ logging.warn("#" * 40)
+ sys.exit(1)
+
+ # Delete the disk cache if any to ensure a clean test run.
+ cachedir = os.path.split(path_utils.TestShellBinaryPath(options.target))[0]
+ cachedir = os.path.join(cachedir, "cache")
+ if os.path.exists(cachedir):
+ shutil.rmtree(cachedir)
+
+ # Include all tests if none are specified.
+ paths = []
+ if args:
+ paths += args
+ if options.test_list:
+ paths += ReadTestFiles(options.test_list)
+ if not paths:
+ paths = ['.']
+ test_runner = TestRunner(options, paths)
+ test_runner.AddTestType(text_diff.TestTextDiff)
+ test_runner.AddTestType(simplified_text_diff.SimplifiedTextDiff)
+ if options.pixel_tests:
+ test_runner.AddTestType(image_diff.ImageDiff)
+ has_new_failures = test_runner.Run()
+ logging.info("Exit status: %d" % has_new_failures)
+ sys.exit(has_new_failures)
+
+if '__main__' == __name__:
+ option_parser = optparse.OptionParser()
+ option_parser.add_option("", "--nohttp", action="store_true", default=False,
+ help="disable http tests")
+ option_parser.add_option("", "--nosvg", action="store_true", default=False,
+ help="disable svg tests")
+ option_parser.add_option("", "--pixel-tests", action="store_true",
+ default=False,
+ help="enable pixel-to-pixel PNG comparisons")
+ option_parser.add_option("", "--results-directory",
+ default="layout-test-results",
+ help="Output results directory source dir,"
+ " relative to Debug or Release")
+ option_parser.add_option("", "--new-baseline", default=None, metavar="DIR",
+ help="save results as new baselines into this "
+ "directory (e.g. layout_test_results/v8), "
+ "overwriting whatever's already there. "
+ "If pixel tests are being run, only image "
+ "baselines will be saved, not text.")
+ option_parser.add_option("", "--noshow-results", action="store_true",
+ default=False, help="don't launch the test_shell"
+ " with results after the tests are done")
+ option_parser.add_option("", "--full-results-html", action="store_true",
+ default=False, help="show all failures in"
+ "results.html, rather than only regressions")
+ option_parser.add_option("", "--num-test-shells",
+ default=1,
+ help="The number of test shells to run in"
+ " parallel. EXPERIMENTAL.")
+ option_parser.add_option("", "--save-failures", action="store_true",
+ default=False,
+ help="Save lists of expected failures and crashes "
+ "and use them in computing regressions.")
+ option_parser.add_option("", "--nocompare-failures", action="store_true",
+ default=False,
+ help="Disable comparison to the last test run. "
+ "When enabled, show stats on how many tests "
+ "newly pass or fail.")
+ option_parser.add_option("", "--time-out-ms",
+ default=None,
+ help="Set the timeout for each test")
+ option_parser.add_option("", "--run-singly", action="store_true",
+ default=False,
+ help="run a separate test_shell for each test")
+ option_parser.add_option("", "--debug", action="store_true", default=False,
+ help="use the debug binary instead of the release "
+ "binary")
+ option_parser.add_option("", "--build-type", default="v8",
+ help="use these test lists and expected results "
+ "('kjs' or 'v8')")
+ option_parser.add_option("", "--target", default="",
+ help="Set the build target configuration (overrides"
+ "--debug)")
+ option_parser.add_option("-v", "--verbose", action="store_true",
+ default=False, help="include debug level logging")
+ option_parser.add_option("", "--startup-dialog", action="store_true",
+ default=False,
+ help="create a dialog on test_shell.exe startup")
+ option_parser.add_option("", "--test-list", action="append",
+ help="read list of tests to run from file",
+ metavar="FILE")
+ options, args = option_parser.parse_args()
+ main(options, args)
diff --git a/webkit/tools/layout_tests/run_webkit_tests.sh b/webkit/tools/layout_tests/run_webkit_tests.sh
new file mode 100755
index 0000000..6ac6696
--- /dev/null
+++ b/webkit/tools/layout_tests/run_webkit_tests.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+system_root=`cygpath "$SYSTEMROOT"`
+export PATH="/usr/bin:$system_root/system32:$system_root:$system_root/system32/WBEM"
+
+exec_dir=$(dirname $0)
+
+"$exec_dir/../../../third_party/python_24/python.exe" \
+ "$exec_dir/run_webkit_tests.py" "$@"
diff --git a/webkit/tools/layout_tests/test_lists/README b/webkit/tools/layout_tests/test_lists/README
new file mode 100644
index 0000000..985ebe2
--- /dev/null
+++ b/webkit/tools/layout_tests/test_lists/README
@@ -0,0 +1,34 @@
+The files in this directory are read by the
+run_webkit_tests.py script and used to determine which tests to run
+and which failures to consider regressions.
+
+If we *never* expect to pass these tests (e.g. test for Apple's objective-c
+API), then add them to tests_ignore.txt. If we expect that we might ever want to
+pass the test, first file a bug, then add it to tests_fixable.txt.
+
+Tests can have the following metatdata associated with them (optional):
+DEFER : We don't expect to pass them in the current release.
+SKIP : We don't want to run them (perhaps because they take too long).
+
+Tests have a build-type (required):
+V8 # The test only fails in V8 build.
+KJS # The test only fails in KJS build.
+
+Tests can also have expections (required):
+PASS
+FAIL
+TIMEOUT
+CRASH
+
+The format of a test is as follows:
+BUILD_TYPE # METADATA : TEST = EXPECTATIONS
+
+For example
+V8 # DEFER : LayoutTests/media = PASS | FAIL
+
+The above means that in the V8 build, all the media tests are flaky and
+we'll defer fixing them for a future release.
+
+Note that tests_fixable.txt and tests_ignored.txt aren't allowed to share any
+lines. Except for skipped tests, all tests are run. In calculating success
+metrics, we ignore tests in tests_ignored.txt and deferred tests.
diff --git a/webkit/tools/layout_tests/test_lists/tests_fixable.txt b/webkit/tools/layout_tests/test_lists/tests_fixable.txt
new file mode 100644
index 0000000..17ce386
--- /dev/null
+++ b/webkit/tools/layout_tests/test_lists/tests_fixable.txt
@@ -0,0 +1,718 @@
+// These tests are expected to fail until we get around to fixing
+// them.
+
+
+// -----------------------------------------------------------------
+// DEBUG ONLY FAILURES!
+// -----------------------------------------------------------------
+
+// Bug 1124548: Copying with no selection is sometimes supposed to work
+// This test also crashes in debug due to an ASSERT. (see bug 1058654)
+V8 | KJS # DEFER : LayoutTests/editing/execCommand/copy-without-selection.html = FAIL | CRASH
+
+// -----------------------------------------------------------------
+// HANGING TESTS
+// -----------------------------------------------------------------
+
+// onload race condition due to poorly designed test.
+// Works fine when run stand-alone. Not needed for Beta.
+V8 | KJS # DEFER : LayoutTests/fast/dom/frame-loading-via-document-write.html = FAIL
+
+// -----------------------------------------------------------------
+// FLAKY TESTS
+// -----------------------------------------------------------------
+
+// This test uses a -webkit-transition-duration and a set timeout, probably
+// just a small race in our timers. Fails once in a while, only on v8.
+V8 # DEFER : LayoutTests/fast/css/transition-color-unspecified.html = PASS | FAIL
+
+// Flaky tests, see bug 877986.
+// WebKit's CSS counters are somewhat broken, thus expected results are failures
+// Our high-precision timers make these tests flakey.
+// We could fork these tests, but we'll just unfork them as soon as
+// our high-precision timers are public.
+V8 | KJS # DEFER : LayoutTests/css2.1/t1204-increment-00-c-o.html = FAIL | PASS
+V8 | KJS # DEFER : LayoutTests/css2.1/t1204-increment-01-c-o.html = FAIL | PASS
+V8 | KJS # DEFER : LayoutTests/css2.1/t1204-increment-02-c-o.html = FAIL | PASS
+V8 | KJS # DEFER : LayoutTests/css2.1/t1204-reset-00-c-o.html = FAIL | PASS
+V8 | KJS # DEFER : LayoutTests/css2.1/t1204-reset-01-c-o.html = FAIL | PASS
+
+// Bug 1143337
+// These tests are here because they fail on the buildbot, but not locally.
+// Seems to be due to one or more timing issues in the tests.
+// These only timeout on the v8-Debug buildbot it seems. (eseidel, 4/28)
+// I'm not seeing this fail locally in v8-Debug or v8-Release (eseidel, 4/25)
+V8 | KJS # LayoutTests/http/tests/security/dataURL/xss-DENIED-to-data-url-in-foreign-domain-subframe-location-change.html = PASS | FAIL
+V8 | KJS # LayoutTests/http/tests/security/dataURL/xss-DENIED-from-data-url-in-foreign-domain-subframe.html = PASS | TIMEOUT
+
+// This are failing for different reasons under our new lighttpd configuration
+// TODO(deanm): Address all of these via lighttpd if possible, otherwise fork.
+// Maybe flaky and need to be forked? Bug 1234761.
+
+// Consistently fails on KJS only
+KJS # LayoutTests/http/tests/security/cross-frame-access-call.html = FAIL
+// Maybe flaky and need to be forked?
+V8 | KJS # LayoutTests/http/tests/security/cross-frame-access-enumeration.html = TIMEOUT
+// Difference in caching headers
+V8 | KJS # LayoutTests/http/tests/xmlhttprequest/cache-override.html = FAIL
+// LightTPD doesn't accept unknown HTTP methods
+V8 | KJS # LayoutTests/http/tests/xmlhttprequest/methods-lower-case.html = TIMEOUT
+V8 | KJS # LayoutTests/http/tests/xmlhttprequest/methods-async.html = TIMEOUT
+// LightTPD doesn't accept unknown HTTP methods and passes CGIs a Content-Type
+// even when a request didn't send the header.
+V8 | KJS # LayoutTests/http/tests/xmlhttprequest/methods.html = FAIL
+
+// -----------------------------------------------------------------
+// TEXT
+// -----------------------------------------------------------------
+
+// This class of test fails because of size differences in text runs.
+// Mostly this is because of international text rendering differences.
+
+// Bug 1124513: the max length is being applied correctly, but the over- and
+// under-lines aren't placed properly over the "x".
+// The under-lines are a cosmetic error which is not necessary to fix for Beta. (eseidel)
+V8 | KJS # DEFER : LayoutTests/fast/forms/input-text-maxlength.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/forms/input-text-paste-maxlength.html = FAIL
+
+// Font differences, requiring overriden metrics, not a real bug, not fixing for Beta
+V8 | KJS # DEFER : LayoutTests/fast/text/international/bidi-AN-after-L.html = FAIL
+
+// Bug: 1145880
+// Parethesis missing, metrics wrong.
+V8 | KJS # DEFER : LayoutTests/fast/text/international/bidi-neutral-run.html = FAIL
+
+// Bug: 628529: complex text effects
+// This is a real bug, but not one we're fixing for Beta.
+V8 | KJS # DEFER : LayoutTests/fast/text/stroking-decorations.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/text/stroking.html = FAIL
+
+// Bug: 1124522
+// Incrorect results, in incorrect international font metrics.
+// Fixing these overrides does not help us to Beta, deffering
+V8 | KJS # DEFER : LayoutTests/fast/text/atsui-multiple-renderers.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/text/atsui-pointtooffset-calls-cg.html = FAIL
+
+// Bug: 1143381
+// This test checks that we hack around a bug in helvetica. We fail to.
+V8 | KJS # DEFER : LayoutTests/fast/text/wide-zero-width-space.html = FAIL
+
+// Font-size differences in international text cause the wrong character
+// to be under the (x,y) click location used by the test. See bug 850411
+// on faking international font sizes like we do for Latin fonts.
+V8 | KJS # DEFER : LayoutTests/fast/text/atsui-rtl-override-selection.html = FAIL
+
+// Bug: 1124542
+// More missing international text overides, not needed for Beta.
+// Capitalization results match Safari, even if "not fully correct"
+V8 | KJS # DEFER : LayoutTests/fast/text/capitalize-boundaries.html = FAIL
+
+// Bug: 1145887
+// Different button line-heights, our behavior looks wrong.
+V8 | KJS # DEFER : LayoutTests/fast/forms/control-restrict-line-height.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/replaced/table-percent-height.html = FAIL
+
+// Bug 992930: Unable to load file:/// URLs from data: URLs.
+V8 | KJS # DEFER : LayoutTests/fast/events/standalone-image-drag-to-editable.html = FAIL
+
+// Bug 1187672. Two font faces should be identical but aren't. Punting SVG.
+V8 | KJS # DEFER : LayoutTests/svg/custom/font-face-simple.svg = FAIL
+
+// -----------------------------------------------------------------
+// URL
+// -----------------------------------------------------------------
+
+// http://b/1089231: Form submission (GET) on non-standard url does not append a query.
+// Defering these tests as the expected behavior is wonky, and shouldn't affect
+// existing apps one way or another.
+
+// Implicit expectation in this test is that you can "set query" on a data URL,
+// and it should replace the first "?" substring. This makes absolutely no sense.
+V8 | KJS # DEFER : LayoutTests/fast/events/stopPropagation-submit.html = FAIL
+
+// Expected results has a terminal "?", since "set query" on about:blank is allowed.
+// This is strange since query should have no meaning in non-standard urls
+V8 | KJS # DEFER : LayoutTests/http/tests/navigation/onload-navigation-iframe-timeout.html = FAIL
+V8 | KJS # DEFER : LayoutTests/http/tests/navigation/onload-navigation-iframe.html = FAIL
+
+// -----------------------------------------------------------------
+// Other
+// -----------------------------------------------------------------
+
+// Bug 865472, this should just need proper pixel test rebaselining.
+V8 # LayoutTests/http/tests/navigation/javascriptlink-frames.html = FAIL
+
+// Bug 1235433, javascript can execute before stylesheets are completely loaded.
+V8 | KJS # DEFER : LayoutTests/http/tests/local/stylesheet-and-script-load-order.html = FAIL
+
+// Bug 1195064, dragging into a textarea is less-ideal as it used to be.
+V8 | KJS # DEFER : LayoutTests/fast/forms/drag-into-textarea.html = FAIL
+
+// Bug: 1143492
+// Window status should always return a string object
+// WebKit does this to match IE, FF also fails this test.
+// Obscure, not sure we care. DEFER for Beta
+V8 | KJS # DEFER : LayoutTests/fast/dom/assign-to-window-status.html = FAIL
+
+// Bug 905894
+// Getting parseerror (probably wrong svn:eol-style)
+// Will be fixed by upstream merge
+V8 | KJS # DEFER : LayoutTests/fast/xsl/xslt-enc16.xml = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/xsl/xslt-enc16to16.xml = FAIL
+
+// Bug: 742182, 845388
+// Mac Safari under certain circumstances automatically places
+// a caret in editable document even when none was requested programatically.
+// We don't intend to copy this feature (at least not for Beta).
+V8 | KJS # DEFER : LayoutTests/editing/selection/designmode-no-caret.html = FAIL
+
+// Bug: 742182, 845388, 960092
+// Platform-specific: simulates command-{arrow} input to modify selection
+// Our Event-Sender isn't robust enough to support this.
+// Not required for Beta. This may also be related to known home/end issues
+// which are intended to be fixed for Beta.
+V8 | KJS # DEFER : LayoutTests/editing/selection/move-begin-end.html = FAIL
+
+// Bug 845400 Miscellaneous layout issues under editing
+V8 | KJS # DEFER : LayoutTests/editing/pasteboard/copy-standalone-image.html = FAIL
+
+// Bug 845400
+// The end result looks right, but the event messages differ.
+// Not critical for Beta, DEFER
+V8 | KJS # DEFER : LayoutTests/editing/pasteboard/paste-xml.xhtml = FAIL
+
+// Bug 849441
+// Directionality of mixed-direction text in selected choice should
+// match that in the <select> option lists.
+// Low priority, unclear if test expectations are correct (see bug)
+V8 | KJS # DEFER : LayoutTests/fast/forms/select-writing-direction-natural.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/text/international/bidi-menulist.html = FAIL
+
+// Bug 850875
+// requires support for layoutTestController.encodeHostName()
+// Not critical for Beta, DEFER
+V8 | KJS # DEFER : LayoutTests/fast/encoding/idn-security.html = FAIL
+
+// Bug 852346
+// Tests link coloring and needs a history dataLayoutTests.
+// This is a test tool problem, not a Chrome problem.
+// Not critical for Beta. DEFER
+V8 | KJS # DEFER : LayoutTests/fast/history/clicked-link-is-visited.html = FAIL
+
+// Bug 1027226
+// Bug 945322: test shell should dump text when
+// layoutTestController.notifyDone() is called
+// Not critical for beta.
+V8 | KJS # DEFER : LayoutTests/editing/selection/drag-in-iframe.html = TIMEOUT
+
+// BUG 938563: occasionally times out (performs about 50 HTTP CGI requests)
+V8 | KJS # DEFER : LayoutTests/http/tests/xmlhttprequest/supported-xml-content-types.html = PASS | TIMEOUT
+
+// Bug 849056
+// We don't support NPN_Enumerate, but don't know of any plugin
+// which depends on that functionality. So we ignore this for beta.
+V8 | KJS # DEFER : LayoutTests/plugins/netscape-enumerate.html = FAIL
+
+// Bug: 767628
+// This fails due to scrolling and event differences.
+V8 | KJS # DEFER : LayoutTests/fast/forms/focus-selection-textarea.html = FAIL
+
+// This tests the screen's pixel depth, which we don't set on the buildbots
+// so it depends on the users settings. Making this a broken test for us.
+// The test must be fixed to not depend on user settings and rebaselined. post-beta.
+V8 | KJS # DEFER : LayoutTests/fast/dom/Window/window-screen-properties.html = FAIL
+
+// Bug: 849060
+// Plugin creation is delayed until after first layout, so
+// setwindow isn't being called.
+V8 | KJS # LayoutTests/plugins/netscape-plugin-setwindow-size.html = FAIL
+
+// gc-6.html failed because the loop in the test didn't allocate enough
+// garbage objects to trigger a GC. It was passing before. The code
+// for triggering GC is unreliable.
+// Not a useful test for us, DEFER
+V8 | KJS # DEFER : LayoutTests/fast/dom/gc-6.html = TIMEOUT
+
+// Bug: 894476
+// It is flaky on V8 build, we need to investigate it.
+// It fails on KJS build. It is not something for beta.
+KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-put.html = FAIL
+// It is flaky on V8 build.
+V8 # LayoutTests/http/tests/security/cross-frame-access-put.html = PASS | FAIL
+
+// V8 specific test
+KJS # SKIP | DEFER : chrome/fast/dom/script_lineno.html = FAIL
+
+// KJS doesn't support cross-domain setting of window.opener.
+KJS # DEFER : chrome/http/tests/misc/set-window-opener-to-null.html = FAIL
+
+// Bug: 1135526
+// I've not seen the failing results, so it's difficult to guess what might be wrong
+// This test flips back and forth on the bots, passes locally (eseidel, 4/28)
+// This test uses setTimeout(foo, 1) to poll window.closed after window.close() is
+// called to only continue once the window is fully closed. It's possible we're
+// setting window.close too early (wild guess).
+// only failing on KJS hence removing V8 (sandholm, 4/29)
+// Defer until after beta since it only fails in KJS.
+KJS # DEFER : LayoutTests/fast/dom/Window/new-window-opener.html = FAIL
+
+// Bug 982602
+// We don't support support window.resizeTo (nor is it planned for Beta)
+V8 | KJS # DEFER : LayoutTests/fast/dom/Window/window-resize-and-move-arguments.html = FAIL
+
+// layoutTestController.setPopupBlockingEnabled() does not exist.
+// Not critical for Beta.
+V8 | KJS # DEFER : LayoutTests/fast/events/open-window-from-another-frame.html = FAIL
+
+// Test expects that when focus is in an iframe and page-up is hit, the parent
+// document is also scrolled
+// IE and FF also "fail" this test. DEFER
+V8 | KJS # DEFER : LayoutTests/fast/frames/iframe-scroll-page-up-down.html = FAIL
+
+// Bug 1082426
+// document.write() pf plain text does not always flush
+// This is a known WebKit bug, https://bugs.webkit.org/show_bug.cgi?id=8961
+V8 | KJS # DEFER : LayoutTests/editing/execCommand/delete-no-scroll.html = FAIL
+
+// TODO: investigate. Broken with webkit merge 28723:29478
+// KJS only, so we don't care for Beta.
+KJS # DEFER : LayoutTests/http/tests/security/frameNavigation/xss-DENIED-plugin-navigation.html = FAIL
+
+// Bug: 879449
+// TODO(joshia): Need some changes to the test shell in order to support
+// Java applet related unit tests. So disable the following for now.
+// These tests should be fixed immediately after the Java applets work is done.
+V8 | KJS # LayoutTests/fast/replaced/applet-disabled-positioned.html = FAIL
+V8 | KJS # LayoutTests/fast/replaced/applet-rendering-java-disabled.html = FAIL
+
+// Bug 1198880
+V8 | KJS # DEFER : LayoutTests/svg/custom/svgsvgelement-ctm.xhtml = FAIL
+
+// Bug 1204878
+V8 | KJS # LayoutTests/http/tests/navigation/post-goback1.html = FAIL
+
+// -----------------------------------------------------------------
+// PENDING TESTS (forked to pending/, need to be sent upstream)
+// -----------------------------------------------------------------
+
+// BUG 792023. See http://bugs.webkit.org/show_bug.cgi?id=15690 and 16494
+// Fixed tests are in pending directory. (The originals still pass consistently
+// in KJS.)
+V8 # DEFER : LayoutTests/dom/html/level2/html/HTMLFrameElement09.html = FAIL
+V8 # DEFER : LayoutTests/dom/html/level2/html/HTMLIFrameElement11.html = FAIL
+
+// Bug 972450: These tests don't work with fast timers due to setTimeout
+// races. Pending versions have these fixed.
+V8 | KJS # DEFER : LayoutTests/fast/history/history_reload.html = PASS | FAIL
+V8 | KJS # DEFER : LayoutTests/fast/repaint/bugzilla-6473.html = PASS | FAIL
+
+// Bug 982608: test had a wrong result for one condition
+V8 | KJS # DEFER : LayoutTests/plugins/destroy-stream-twice.html = FAIL
+
+// This test has been modified and placed in pending, so we ignore the original
+// until we get our modification into WebKit.
+V8 | KJS # DEFER : LayoutTests/security/block-test.html = PASS | FAIL
+
+// Bug 1124522
+// Test forked into pending and fixed.
+V8 # DEFER : LayoutTests/fast/js/function-toString-parentheses.html = FAIL
+
+// Bug 1132721. Forked to pending/fast/encoding/script-in-head.html
+// Should get this change pushed upstream and then unfork.
+// Stopped failing
+// V8 # DEFER : LayoutTests/fast/encoding/script-in-head.html = PASS | FAIL | TIMEOUT
+
+// Bug 1143337. Forked to pending/http/tests/security/.
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-port-explicit-domain.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-port.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-protocol-explicit-domain.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/cross-frame-access-protocol.html = PASS | FAIL | TIMEOUT
+V8 | KJS # DEFER : LayoutTests/http/tests/security/protocol-compare-case-insensitive.html = PASS | FAIL | TIMEOUT
+
+// Bug 1199617. Forked to pending/svg/carto.net/window.svg.
+// Test did not wait for all created functions to be called via
+// setTimeout.
+V8 | KJS # DEFER : LayoutTests/svg/carto.net/window.svg = PASS | FAIL
+
+// Bug 1064038. Image with border="1" drawn without the border.
+V8 | KJS # DEFER : pending/fast/forms/image-border.html = FAIL
+
+// Bug 1055396. Vertical scrollbar created when there is no overflow.
+V8 | KJS # DEFER : pending/fast/forms/textarea-scrollbar-height.html = FAIL
+
+// Bug 1059184. Dashed border-right isn't drawn on tall elements.
+V8 | KJS # DEFER : pending/css/border-height.html = FAIL
+
+// -----------------------------------------------------------------
+// DEFERRED TESTS (deferred until a future release)
+// -----------------------------------------------------------------
+
+// Bug 1203341: Requires a working postMessage implementation
+V8 # DEFER : LayoutTests/http/tests/security/cross-frame-access-delete.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/security/cross-frame-access-history-put.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/security/cross-frame-access-location-put.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/security/postMessage/domain-and-uri-unaffected-by-base-tag.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/security/postMessage/domain-unaffected-by-document-domain.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/security/postMessage/javascript-page-still-sends-domain.html = TIMEOUT
+V8 # DEFER : LayoutTests/http/tests/messaging/cross-domain-message-send.html = TIMEOUT
+
+// Bug 1135948: Fails because we cannot call plugins as functions.
+V8 # DEFER : LayoutTests/plugins/bindings-test.html = FAIL
+
+// Bug 1124435: deal with the deletion UI in a later release.
+V8 | KJS # DEFER : LayoutTests/editing/deleting/deletionUI-single-instance.html = FAIL
+
+// Bug 871718: This test loads data: URLs into frames and sets queries on then.
+// This is totally broken. This layout test should be rewitten so that the
+// subframes are not data URLs (probably we want files in the resources dir.).
+V8 | KJS # DEFER : LayoutTests/fast/encoding/char-encoding.html = TIMEOUT
+
+// Bug 1130795: since we don't have Aqua-themed controls, don't ignore the
+// box-shadow properties of controls that request Aqua theming. But since Aqua
+// controls are rare on the web, defer this fix.
+V8 | KJS # DEFER : LayoutTests/fast/forms/box-shadow-override.html = FAIL
+
+// These tests are not valid: the so-called expected results are not known to
+// be correct. See bug 849072.
+// TODO(ojan): They are *our* tests.
+// It seems silly to skip our own tests when we can change/delete them.
+// I'm marking them as deferred for now, but we should do something with them.
+V8 | KJS # DEFER | SKIP : chrome/http/mime = PASS
+
+// Bug: 916857: These tests fail because of <audio> and <video>?
+// Removed from the skip-list since they consistently fail quickly.
+V8 | KJS # DEFER : LayoutTests/http/tests/media/video-play-stall.html = FAIL
+V8 | KJS # DEFER : LayoutTests/http/tests/media/video-play-stall-seek.html = FAIL
+V8 | KJS # DEFER : LayoutTests/http/tests/media/video-seekable-stall.html = FAIL
+V8 | KJS # DEFER : LayoutTests/http/tests/media/remove-while-loading.html = FAIL
+
+// We don't support the storage APIs. Some of the them hang.
+V8 | KJS # DEFER | SKIP : LayoutTests/storage = PASS
+
+// Fails due to storage APIs not implemented. See bug 1124568. Might be worth
+// re-baselining temporarily so the rest of the conditions are still tested.
+V8 | KJS # DEFER : LayoutTests/fast/dom/Window/window-function-name-getter-precedence.html = FAIL
+
+// These tests all time out, which makes running the suite take too long if
+// they're included. See bug 916857 to investigate <audio> and <video>.
+V8 | KJS # DEFER | SKIP : LayoutTests/media = TIMEOUT
+
+// Bug 850287: Need better Thai line-breaking. Not a high priority, because
+// it's acceptable as it is.
+V8 | KJS # DEFER : LayoutTests/fast/text/international/thai-line-breaks.html = FAIL
+
+// Bug 941049: Function arguments object is copied for each access.
+V8 # DEFER : LayoutTests/fast/js/kde/function_arguments.html = FAIL
+
+// Bug 1155674: Test sometimes fails on V8 in debug mode, because it's very
+// timing dependent. We should consider rewriting the test case to give us
+// more wriggle room timing wise (especially for debug builds). Fixing the
+// issue now is not going to improve product quality for beta.
+V8 # DEFER : LayoutTests/fast/forms/search-event-delay.html = PASS | FAIL
+
+
+// The following tests (up to the ---- line below) need to add new methods to
+// layoutTestController in test_shell since WebKit add those methods to its
+// layoutTestController in DumpRenderTree tools.
+
+// BUG 973468: Need a setAuthorAndUserStylesEnabled method in
+// layoutTestController. Now we have preference to enable/disable user
+// styles(not work now), we still need to add a preference to enable/disable
+// styles of both author and user.
+// Deferring, we don't support user-controlled UA stylesheets (in beta)
+// Actually, gonna SKIP because it causes an additional error message in:
+// LayoutTests/fast/css/display-none-inline-style-change-crash.html somehow
+// the message is dumped after the #EOF, which causes an additional
+// error in the header of the following test.
+V8 | KJS # SKIP | DEFER : LayoutTests/fast/css/disabled-author-styles.html = FAIL
+
+// ------------------------------------------------------------------------- //
+
+// Bug: 924387, 1058654
+// Broken until we fix our port to support remote TTF fonts and SVG Fonts
+// GDI @font-face support has been implemented upstream, but we don't plan
+// to fork to add support for @font-face for Beta.
+// upstream: http://trac.webkit.org/projects/webkit/changeset/31507
+V8 | KJS # DEFER : LayoutTests/fast/css/font-face-multiple-remote-sources.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/css/font-face-remote.html = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/custom/font-face-cascade-order.svg = FAIL
+
+// Bug: 1007391
+// These hit a not-implemented code-path in @font-face code
+// Fixing this should not be required for beta.
+V8 | KJS # DEFER : LayoutTests/fast/css/font-face-implicit-local-font.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/css/font-face-unicode-range.html = FAIL
+
+// Bug: 1110028
+// The v8 bindings allow shadowing of all properties on the global object. If you use
+// 'var prop = value' you will get a new variable named prop that shadows builtin properties
+// on the global object. This behavior is consistent and I'm reluctant to make the
+// massive change that would be needed to implement the inconsistent handling of this
+// that KJS has (some properties can be shadowed and others can't). This should have
+// low priority.
+// We currently match IE, the plan is to convince KJS to change post-beta. DEFER (eseidel, 4/25)
+V8 # DEFER : LayoutTests/fast/dom/Window/window-property-shadowing-name.html = TIMEOUT
+V8 # DEFER : LayoutTests/fast/js/var-declarations-shadowing.html = FAIL
+
+// This test expects weird behavior of __defineGetter__ on the
+// window object. It expects variables introduced with 'var x = value'
+// to behave differently from variables introduced with 'y = value'.
+// This just seems wrong and should have very low priority.
+// Agreed, not required for Beta, we can debate this with WebKit post Beta, (eseidel, 4/25)
+V8 # DEFER : LayoutTests/fast/dom/getter-on-window-object2.html = FAIL
+
+// Bug: 1042653
+// We don't support WebKit-Editing-Delete-Button
+// We've chosen not to for Beta. DEFER
+V8 | KJS # DEFER : LayoutTests/editing/deleting/5408255.html = FAIL
+
+// Bug: 845337
+// Missing two callbacks:
+// -EDITING DELEGATE: shouldBeginEditingInDOMRange:range ...
+// -EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
+V8 | KJS # LayoutTests/editing/pasteboard/testcase-9507.html = FAIL
+
+// Bug: 1042838
+// User stylesheets not currently supported by chrome.
+// Webkit supports them by doing filesystem operations directly.
+// This is disallowed in a sandboxed renderer. The test fails in test_shell.exe because the
+// necessary filesystem stubs are notImplemented(), and would need to be proxied through the browser
+V8 | KJS # DEFER : LayoutTests/http/tests/security/local-user-CSS-from-remote.html = FAIL
+
+// Extra space at end of test results. Since this is a crash test, a
+// FAIL here is just as good as running the test normally
+// Not sure why it passes (frequently on V8, rarely on KJS). See bug 1126050.
+// FAIL results from an extra newline at the top of the results
+// the checked in results do not include the PASS text
+// fixing this test file to be reliable does not help Beta. DEFERing
+V8 | KJS # DEFER : LayoutTests/http/tests/navigation/changing-frame-hierarchy-in-onload.html = PASS | FAIL
+
+// -----------------------------------------------------------------
+// SVG TESTS
+// -----------------------------------------------------------------
+
+// BUG: 992321
+// SVG layout tests have just been enabled. The failures are listed
+// below, and need to be investigated, categorized, and (one hopes)
+// fixed.
+
+// Keep score here!
+//
+// P: Passes
+// S: Pass simplified diff (probable nonbugs)
+// F: Failures (in simplified as well as full text)
+// X: Crashes/hangs
+// T: Total # of tests
+//
+// (note 5-22-08: there should probably be a column for image diff
+// failures as well. Some of the font tests seem to be flaky and
+// return inconsistent image results.)
+//
+// P S F X T
+// 1-18-08 473 87 125 3 688
+// 1-29-08 492 86 106 4 688
+// 2-05-08 513 97 83 1 694
+// 4-17-08 520 88 85 0 :) 693
+// 4-21-08 526 87 80 0 693
+// 4-22-08 540 138 29 0 707
+// 5-05-08 551 138 15 0 707
+// 5-09-08 554 139 16 0 712
+// 5-16-08 587 111 14 0 712
+// 5-22-08 641 58 7 0 712
+//
+
+// The following tests fail because SVG animation is not yet implemented
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-06-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-07-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-08-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-28-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-30-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-33-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-36-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-37-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-41-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-78-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-80-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-81-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-82-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/animate-elem-83-t.svg = FAIL
+
+// This test fails because SVG filters are not implemented
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/filters-example-01-b.svg = FAIL
+
+// These test fail full text diff (but not simplified diff) most likely due
+// to differing implementations of SVG fonts. They may or may not represent real
+// bugs which need fixin'
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/smallFonts.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textBiDi.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textGlyphOrientationHorizontal.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textOnPath.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textOnPath2.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textOnPath3.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/textOnPathSpaces.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/verticalText.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/batik/text/verticalTextOnPath.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/custom/path-textPath-simulation.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/text/text-fonts-01-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/text/text-intro-05-t.svg = FAIL
+V8 | KJS # DEFER : LayoutTests/svg/text/text-path-01-b.svg = FAIL
+
+// This test is failing because Apple's results appear to be bogus. Will let them know.
+V8 | KJS # DEFER : LayoutTests/svg/custom/gradient-stop-style-change.svg = FAIL
+
+// This test fails because of an oddity in stroke width calculation. A
+// line is stroked horizontally from Y=100 with stroke width 100, we consider
+// it to be occupying the scan lines from Y=50 to Y=149 inclusive, which
+// is a total of 100 lines. Apple expects it to be occupying Y=150 as
+// well. The specification isn't very specific on which behavior is correct,
+// but I feel like ours makes more sense.
+V8 | KJS # DEFER : LayoutTests/svg/custom/stroke-width-click.svg = FAIL
+
+// These tests all pass simplified diff, but fail full text diffs due to
+// positions and/or widths that deviate from Apple's expectations by varying
+// degrees. These are almost certainly nonbugs, but won't be rebased until
+// we can say for sure.
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/extend-namespace-01-f.svg = FAIL // 12 numbers differ, by an absolute total of 73.93
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/fonts-glyph-02-t.svg = FAIL // 1 numbers differ, by an absolute total of 10.00
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/metadata-example-01-b.svg = FAIL // 11 numbers differ, by an absolute total of 1.08
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-01-t.svg = FAIL // 10 numbers differ, by an absolute total of 15.39
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-02-t.svg = FAIL // 20 numbers differ, by an absolute total of 327.17
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-03-f.svg = FAIL // 14 numbers differ, by an absolute total of 58.82
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-10-t.svg = FAIL // 21 numbers differ, by an absolute total of 23.96
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-12-t.svg = FAIL // 8 numbers differ, by an absolute total of 119.28
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/paths-data-15-t.svg = FAIL // 6 numbers differ, by an absolute total of 21.34
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/struct-group-03-t.svg = FAIL // 8 numbers differ, by an absolute total of 3.48
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/text-fonts-01-t.svg = FAIL // 3 numbers differ, by an absolute total of 42.00
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/text-intro-05-t.svg = FAIL // 18 numbers differ, by an absolute total of 237.00
+V8 | KJS # DEFER : LayoutTests/svg/W3C-SVG-1.1/text-path-01-b.svg = FAIL // 16 numbers differ, by an absolute total of 67.92
+V8 | KJS # DEFER : LayoutTests/svg/custom/control-points-for-S-and-T.svg = FAIL // 6 numbers differ, by an absolute total of 31.36
+V8 | KJS # DEFER : LayoutTests/svg/custom/dasharrayOrigin.svg = FAIL // 2 numbers differ, by an absolute total of 0.20
+V8 | KJS # DEFER : LayoutTests/svg/custom/linking-a-03-b-all.svg = FAIL // 4 numbers differ, by an absolute total of 0.06
+V8 | KJS # DEFER : LayoutTests/svg/custom/linking-a-03-b-viewBox-transform.svg = FAIL // 4 numbers differ, by an absolute total of 0.06
+V8 | KJS # DEFER : LayoutTests/svg/custom/linking-a-03-b-viewBox.svg = FAIL // 2 numbers differ, by an absolute total of 0.04
+V8 | KJS # DEFER : LayoutTests/svg/custom/marker-changes.svg = FAIL // 6 numbers differ, by an absolute total of 5.00
+V8 | KJS # DEFER : LayoutTests/svg/custom/use-css-events.svg = FAIL // 5 numbers differ, by an absolute total of 27.16
+V8 | KJS # DEFER : LayoutTests/svg/custom/use-on-symbol-inside-pattern.svg = FAIL // 11 numbers differ, by an absolute total of 4.69
+V8 | KJS # DEFER : LayoutTests/svg/hixie/perf/001.xml = FAIL // 274 numbers differ, by an absolute total of 157.52
+V8 | KJS # DEFER : LayoutTests/svg/hixie/perf/002.xml = FAIL // 274 numbers differ, by an absolute total of 157.52
+V8 | KJS # DEFER : LayoutTests/svg/hixie/perf/007.xml = FAIL // 745 numbers differ, by an absolute total of 37.97
+V8 | KJS # DEFER : LayoutTests/svg/hixie/shapes/path/001.xml = FAIL // 6 numbers differ, by an absolute total of 100.56
+V8 | KJS # DEFER : LayoutTests/svg/hixie/text/003.html = FAIL // 1 numbers differ, by an absolute total of 1.00
+V8 | KJS # DEFER : LayoutTests/svg/hixie/text/003a.xml = FAIL // 1 numbers differ, by an absolute total of 1.00
+V8 | KJS # DEFER : LayoutTests/svg/hixie/viewbox/preserveAspectRatio/001.xml = FAIL // 2 numbers differ, by an absolute total of 18.00
+V8 | KJS # DEFER : LayoutTests/svg/hixie/viewbox/preserveAspectRatio/002.xml = FAIL // 3 numbers differ, by an absolute total of 3.00
+
+// This is an interesting one.
+// The test has an error which causes it to output a message to the console.
+// Apple's expected results include this error. Actually, Apple's expected
+// results include the error twice, because (apparently unique among console
+// debugging messages) it's generated during rendering. The problem is that
+// we end up invoking the renderer twice when we're doing pixel tests, once
+// otherwise, so we get different results depending on whether pixel tests are
+// enabled. (Neither matches Apple's exactly... when we have pixel tests on,
+// the duplicate error message ends up at the end of the file, while Apple's
+// results have them both at the top.) But this test doesn't represent a real
+// failure in any way and nothing needs to be done to fix it. I'm leaving it
+// in tests_fixable in case we change the test shell's webview delegate to
+// ignore console messages while doing a pixel dump
+V8 | KJS # DEFER : LayoutTests/svg/custom/clip-path-referencing-use2.svg = FAIL
+
+// Bug 1107191. These flakily fail image diffs. Punting on SVG for now.
+// Note that the "expected" images checked in are not necessarily correct.
+V8 # DEFER : LayoutTests/svg/W3C-SVG-1.1/text-text-03-b.svg = PASS | FAIL
+V8 # DEFER : LayoutTests/svg/batik/text/textDecoration2.svg = PASS | FAIL
+V8 # DEFER : LayoutTests/svg/batik/text/textFeatures.svg = PASS | FAIL
+V8 # DEFER : LayoutTests/svg/batik/text/textProperties.svg = PASS | FAIL
+V8 # DEFER : LayoutTests/svg/batik/text/textStyles.svg = PASS | FAIL
+V8 # DEFER : LayoutTests/svg/text/text-text-03-b.svg = PASS | FAIL
+// These consistently fail with image diffs.
+V8 # DEFER : LayoutTests/svg/text/text-deco-01-b.svg = FAIL
+V8 # DEFER : LayoutTests/svg/W3C-SVG-1.1/text-deco-01-b.svg = FAIL
+
+//
+// -----------------------------------------------------------------
+// End of SVG tests
+// -----------------------------------------------------------------
+
+// Bug: 1026885
+// Following tests are failing because Chrome does not allow file url
+// to access non-file urls.
+V8 | KJS # DEFER : LayoutTests/editing/selection/4960137.html = FAIL
+V8 | KJS # DEFER : LayoutTests/editing/selection/cleared-by-relayout.html = FAIL
+V8 | KJS # DEFER : LayoutTests/editing/selection/inactive-selection.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/dom/Element/offsetLeft-offsetTop-body-quirk.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/dom/HTMLObjectElement/object-as-frame.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/dom/clientWidthAfterDocumentIsRemoved.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/dom/wrapper-classes.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/frames/frame-src-attribute.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/leaks/002.html = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/dom/gc-7.html = TIMEOUT
+V8 | KJS # DEFER : LayoutTests/fast/frames/frame-set-same-location.html = TIMEOUT
+V8 | KJS # DEFER : LayoutTests/fast/frames/frame-set-same-src.html = TIMEOUT
+V8 | KJS # DEFER : LayoutTests/fast/frames/hover-timer-crash.html = TIMEOUT
+
+// Bug: 1045048
+// We haven't implemented GCController in test shell
+// Not critical for Beta. We have window.gc() for other tests.
+V8 | KJS # DEFER : LayoutTests/fast/js/garbage-collect-after-string-appends.html = FAIL
+
+// Bug: 1115062
+// These fail the pixel tests in debug mode because they have
+// unpainted space (filled red in Debug but not in Release).
+// These have been filed upstream, and should be deferred from Beta
+// https://bugs.webkit.org/show_bug.cgi?id=8423
+V8 # DEFER : LayoutTests/tables/mozilla_expected_failures/bugs/bug178855.xml = PASS | FAIL
+V8 # DEFER : LayoutTests/fast/flexbox/flex-hang.html = PASS | FAIL
+
+// We expect this to fail because we can't force a GC on KJS.
+// Not sure why it crashes.
+// Once it stops crashing, move back to ignore list.
+KJS # chrome/plugins/refcount-leaks.html = FAIL | CRASH
+
+// Bug: 1112288
+// This test fails in KJS release build.
+// Since this failure is KJS only, we don't care for Beta.
+KJS # DEFER : LayoutTests/fast/js/kde/Number.html = FAIL
+
+// Bug: 1010703
+// Fails only for KJS, so we don't care for Beta.
+KJS # DEFER : chrome/fast/events/nested-window-event.html = FAIL
+
+// Fails only for KJS, so we don't care for Beta.
+KJS # DEFER : chrome/fast/dom/xss-DENIED-javascript-variations.html = FAIL
+
+// Bug: 1093606
+// Crashes both Chrome and Safari. We don't care about KJS for beta.
+KJS # DEFER : chrome/plugins/nested-plugin-objects.html = PASS | CRASH
+
+// Bug: 1155685
+// Flaky, but only on KJS. We don't care about KJS for beta.
+KJS # DEFER : pending/fast/dom/DOMImplementation/singleton-modifications.html = FAIL | PASS
+
+// Bug: 1166260
+V8 # DEFER : LayoutTests/fast/canvas/gradient-empty-path.html = FAIL
+
+// Bug: 1166644
+// Fails on webkit windows as well.
+V8 | KJS # DEFER : LayoutTests/fast/events/attempt-scroll-with-no-scrollbars.html = FAIL
+
+// Bug: 1170198
+// Intentionally failing to avoid XML External Entity exploit
+// until https://bugs.webkit.org/show_bug.cgi?id=19199 is fixed.
+V8 | KJS # DEFER : LayoutTests/fast/parser/external-entities-in-xslt.xml = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/xsl/dtd-in-source-document.xml = FAIL
+V8 | KJS # DEFER : LayoutTests/fast/xsl/xslt-second-level-import.xml = FAIL
+
+// Bug 1226853: Fails on v8-latest after const changes (r123300). Test
+// has been rebaselined (don't declare const x twice).
+V8 # DEFER : LayoutTests/fast/js/const.html = FAIL
+
+// Bug 1237779
+// gc-2.html tests that a DOM tree out of document should be kept alive if
+// one of nodes is reachable from JS side. V8 binding fails because it does
+// not trace DOM objects not in a document.
+// Also the way to trigger GC is not reliable, that's why sometimes it is
+// passing and sometimes not.
+V8 # DEFER : LayoutTests/fast/dom/gc-2.html = FAIL | PASS
diff --git a/webkit/tools/layout_tests/test_lists/tests_ignored.txt b/webkit/tools/layout_tests/test_lists/tests_ignored.txt
new file mode 100644
index 0000000..dc5de8e
--- /dev/null
+++ b/webkit/tools/layout_tests/test_lists/tests_ignored.txt
@@ -0,0 +1,177 @@
+// These tests will be run (unless skipped), but do not expect ever to pass
+// them. They use platform-specific conventions, or features we have decided
+// never to support.
+
+// -----------------------------------------------------------------
+// SKIPPED TESTS
+// -----------------------------------------------------------------
+
+// XHTML tests. See bug 793944. These tests seem like they work, but
+// only because the expected output expects to see JS errors. There is
+// no point in running these tests, because they are giving us a false
+// sense of testing that isn't really happening. Furthermore, since they
+// appear to pass if we do try to run them, we can't even list them as
+// permanently expected to fail.
+V8 | KJS # SKIP : LayoutTests/fast/dom/xmlhttprequest-get.xhtml = PASS
+V8 | KJS # SKIP : LayoutTests/fast/xpath/nsresolver-bad-object.xhtml = PASS
+V8 | KJS # SKIP : LayoutTests/fast/xpath/nsresolver-exception.xhtml = PASS
+V8 | KJS # SKIP : LayoutTests/fast/xpath/nsresolver-function.xhtml = PASS
+V8 | KJS # SKIP : LayoutTests/fast/xpath/nsresolver-object.xhtml = PASS
+V8 | KJS # SKIP : LayoutTests/dom/xhtml = PASS
+
+// Fails due to different window.close() rules. See bug 753420. We need
+// to decide whether we ever expect to pass this. Now also timing out.
+V8 | KJS # SKIP : LayoutTests/fast/dom/open-and-close-by-DOM.html = FAIL
+
+// Fails because we use MIME names for charset while webkit uses IANA names.
+// Instead of this, we added the corresponding test in chrome with the
+// MIME name (EUC-JP) in the expected result.
+V8 | KJS # SKIP : LayoutTests/fast/encoding/hanarei-blog32-fc2-com.html = FAIL
+
+// Skip because of WebKit bug 18512. These bugs "poison" future tests, causing
+// all SVG objects with fill="red" to be rendered in green.
+V8 | KJS # SKIP : LayoutTests/svg/custom/fill-SVGPaint-interface.svg = PASS
+V8 | KJS # SKIP : LayoutTests/svg/custom/getPresentationAttribute.svg = PASS
+
+// -----------------------------------------------------------------
+// FAILING TESTS
+// -----------------------------------------------------------------
+
+// Bug: 1137420
+// We don't intend to pass all of these cases, so this is an expected fail.
+// Window resizing is not implemented in chrome.
+V8 | KJS # LayoutTests/fast/dom/Window/window-resize.html = FAIL
+
+// Chrome uses different keyboard accelerators from those used by Safari, so
+// these tests will always fail.
+V8 | KJS # LayoutTests/editing/pasteboard/emacs-cntl-y-001.html = FAIL
+V8 | KJS # LayoutTests/editing/pasteboard/emacs-ctrl-a-k-y.html = FAIL
+V8 | KJS # LayoutTests/editing/pasteboard/emacs-ctrl-k-y-001.html = FAIL
+V8 | KJS # LayoutTests/editing/input/emacs-ctrl-o.html = FAIL
+
+// These tests check for very kjs-specific garbage collector behavior. Gc-8
+// tests behavior that makes no sense for us to implement. Gc-10 makes sense
+// but would have to be implemented much differently to work in v8.
+V8 | KJS # LayoutTests/fast/dom/gc-8.html = FAIL
+V8 | KJS # LayoutTests/fast/dom/gc-10.html = FAIL
+
+// This fails because we're missing various useless apple-specific
+// properties on the window object.
+// This test also timeouts in Debug mode. See bug 1058654.
+V8 | KJS # LayoutTests/fast/dom/Window/window-properties.html = FAIL | TIMEOUT
+
+// Safari specific test to ensure that JavaScript errors aren't logged when in
+// private browsing mode.
+V8 | KJS # LayoutTests/http/tests/security/cross-frame-access-private-browsing.html = FAIL
+
+// Chrome uses different keyboard accelerators from those used by Safari, so
+// these tests will always fail.
+// TODO(ericroman): can the following 2 tests be removed from this list, since they pass?
+V8 | KJS # LayoutTests/fast/events/keydown-1.html = FAIL
+V8 | KJS # LayoutTests/fast/events/option-tab.html = FAIL
+
+// Chrome does not support WebArchives (just like Safari for Windows).
+// See bug 761653.
+V8 | KJS # LayoutTests/webarchive = FAIL
+V8 | KJS # LayoutTests/svg/webarchive = FAIL
+V8 | KJS # LayoutTests/svg/custom/image-with-prefix-in-webarchive.svg = FAIL
+
+// Bug 932737
+V8 | KJS # LayoutTests/webarchive/loading/test-loading-archive.html = TIMEOUT
+
+// Mac-specific stuff
+// Don't run the platform/mac* tests
+V8 | KJS # LayoutTests/platform = FAIL | PASS
+
+// Ignored because we do not have OBJC bindings
+V8 | KJS # LayoutTests/editing/pasteboard/paste-RTFD.html = FAIL
+V8 | KJS # LayoutTests/editing/pasteboard/paste-TIFF.html = FAIL
+V8 | KJS # LayoutTests/plugins/jsobjc-dom-wrappers.html = FAIL
+V8 | KJS # LayoutTests/plugins/jsobjc-simple.html = FAIL
+V8 | KJS # LayoutTests/plugins/root-object-premature-delete-crash.html = FAIL
+V8 | KJS # LayoutTests/plugins/throw-on-dealloc.html = FAIL
+V8 | KJS # LayoutTests/plugins/undefined-property-crash.html = FAIL
+
+// Uses __apple_runtime_object
+V8 | KJS # LayoutTests/plugins/call-as-function-test.html = FAIL
+
+// Ignore test because it tries to load .pdf files in <img> tags.
+V8 | KJS # LayoutTests/fast/images/pdf-as-image-landscape.html = FAIL
+V8 | KJS # LayoutTests/fast/images/pdf-as-image.html = FAIL
+V8 | KJS # LayoutTests/fast/replaced/pdf-as-image.html = FAIL
+
+// Uses Option-tab key to circle through form elements. Will not work on
+// Windows.
+V8 | KJS # LayoutTests/fast/events/frame-tab-focus.html = FAIL
+
+// Bug 853268: Chrome doesn't call the willCacheResponse callback (a method
+// of ResourceHandleClient). That function is Mac-specific.
+V8 | KJS # LayoutTests/http/tests/misc/willCacheResponse-delegate-callback.html = FAIL
+
+// Checks for very kjs-specific garbage collector
+// behavior. Gc-9 is completely braindamaged; it tests that certain
+// properties are reset by the garbage collector. It looks to pass recently.
+V8 # LayoutTests/fast/dom/gc-9.html = PASS | FAIL
+
+// This test checks that ((new Error()).message is undefined, which is
+// a direct contradiction of the javascript spec 15.11.4.3 which
+// says that it must be a string.
+V8 # LayoutTests/fast/js/kde/evil-n.html = FAIL
+
+// This test is broken. The regular expression used contains an error
+// which kjs swallows and returns false, which is the expected result,
+// but for which we issue a syntax error.
+V8 # LayoutTests/fast/js/code-serialize-paren.html = FAIL
+
+// These tests check for a kjs-specific extension, that source file
+// name and line numbers are available as properties on exception
+// objects. We handle error positions differently.
+V8 # LayoutTests/fast/js/exception-linenums-in-html-1.html = FAIL
+V8 # LayoutTests/fast/js/exception-linenums-in-html-2.html = FAIL
+V8 # LayoutTests/fast/js/exception-linenums.html = FAIL
+
+// These tests rely on specific details of decompilation of
+// functions. V8 always returns the source code as written; there's
+// no decompilation or pretty printing involved except for
+// certain "native" functions where the V8 output does not include
+// newline characters. This is working as intended and we don't care
+// if the tests pass or fail.
+V8 # LayoutTests/fast/js/function-names.html = FAIL | PASS
+
+// WebKit has moved/changed this test upstream
+// https://bugs.webkit.org/show_bug.cgi?id=18681
+// We will pass the new one after we merge
+KJS | V8 # LayoutTests/http/tests/incremental/slow-utf8-css.pl = FAIL
+
+// -----------------------------------------------------------------
+// CHROME REWRITTEN TESTS
+// -----------------------------------------------------------------
+
+// These tests have been rewritten, with the original being ignored,
+// because they were written in ways which are not cross-browser.
+// (e.g. they expect implementation-dependent strings in output)
+V8 # LayoutTests/fast/js/date-proto-generic-invocation.html = FAIL
+V8 # LayoutTests/fast/js/kde/function.html = FAIL
+V8 # LayoutTests/fast/js/kde/inbuilt_function_tostring.html = FAIL
+
+// This test has been rewritten, with the original being ignored,
+// because it depends on a specific enumeration order of properties
+// on dom objects.
+//
+// The rewritten test chrome/fast/dom/domListEnumeration.html tests
+// the exact same thing, but sorts the elements explicitly before
+// comparing.
+V8 # LayoutTests/fast/dom/domListEnumeration.html = FAIL
+
+// Bug 849085: we're taking a different approach on this test than
+// Webkit does.
+KJS | V8 # SKIP : LayoutTests/plugins/get-url-with-blank-target.html = FAIL
+
+// This test doesn't work on the bbot. Works locally.
+KJS | V8 # SKIP : chrome/http/tests/plugins/get-file-url.html = FAIL | PASS | TIMEOUT
+
+
+// These tests tests V8 bindings only, KJS needs to re-baseline the output.
+// See Bug 1205552
+KJS # SKIP : chrome/fast/dom/set-document-body-no-crash.html = FAIL
+KJS # SKIP : chrome/fast/dom/set-table-head-no-crash.html = FAIL
diff --git a/webkit/tools/layout_tests/test_types/__init__.py b/webkit/tools/layout_tests/test_types/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webkit/tools/layout_tests/test_types/__init__.py
diff --git a/webkit/tools/layout_tests/test_types/image_diff.py b/webkit/tools/layout_tests/test_types/image_diff.py
new file mode 100644
index 0000000..296a1d2
--- /dev/null
+++ b/webkit/tools/layout_tests/test_types/image_diff.py
@@ -0,0 +1,133 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Compares the image output of a test to the expected image output.
+
+Compares hashes for the generated and expected images. If the output doesn't
+match, returns FailureImageHashMismatch and outputs both hashes into the layout
+test results directory.
+"""
+
+import errno
+import os
+import shutil
+
+from layout_package import path_utils
+from layout_package import test_failures
+from test_types import test_type_base
+
+class ImageDiff(test_type_base.TestTypeBase):
+ def _CopyOutputPNGs(self, filename, actual_png, expected_png):
+ """Copies result files into the output directory with appropriate names.
+
+ Args:
+ filename: the test filename
+ actual_png: path to the actual result file
+ expected_png: path to the expected result file
+ """
+ self._MakeOutputDirectory(filename)
+ actual_filename = self.OutputFilename(filename, "-actual-win.png")
+ expected_filename = self.OutputFilename(filename, "-expected.png")
+
+ shutil.copyfile(actual_png, actual_filename)
+ try:
+ shutil.copyfile(expected_png, expected_filename)
+ except IOError, e:
+ # A missing expected PNG has already been recorded as an error.
+ if errno.ENOENT != e.errno:
+ raise
+
+ def _SaveBaselineFiles(self, filename, dest_dir, png_path, checksum):
+ """Saves new baselines for the PNG and checksum.
+
+ Args:
+ filename: test filename
+ dest_dir: outer directory into which the results should be saved.
+ png_path: path to the actual PNG result file
+ checksum: value of the actual checksum result
+ """
+ png_file = open(png_path, "rb")
+ png_data = png_file.read()
+ png_file.close()
+ self._SaveBaselineData(filename, dest_dir, png_data, ".png")
+ self._SaveBaselineData(filename, dest_dir, checksum, ".checksum")
+
+ def CompareOutput(self, filename, proc, output, test_args):
+ """Implementation of CompareOutput that checks the output image and
+ checksum against the expected files from the LayoutTest directory.
+ """
+ failures = []
+
+ # If we didn't produce a hash file, this test must be text-only.
+ if test_args.hash is None:
+ return failures
+
+ # If we're generating a new baseline, we pass.
+ if test_args.new_baseline:
+ self._SaveBaselineFiles(filename, test_args.new_baseline,
+ test_args.png_path, test_args.hash)
+ return failures
+
+ # Compare hashes.
+ expected_hash_file = path_utils.ExpectedFilename(filename,
+ '.checksum',
+ self._custom_result_id)
+
+ expected_png_file = path_utils.ExpectedFilename(filename,
+ '.png',
+ self._custom_result_id)
+
+ try:
+ expected_hash = open(expected_hash_file, "r").read()
+ except IOError, e:
+ if errno.ENOENT != e.errno:
+ raise
+ expected_hash = ''
+
+ if test_args.hash != expected_hash:
+ # TODO(pamg): If the hashes don't match, use the image_diff app to
+ # compare the actual images, and report a different error if those do
+ # match.
+ if expected_hash == '':
+ failures.append(test_failures.FailureMissingImageHash(self))
+ else:
+ failures.append(test_failures.FailureImageHashMismatch(self))
+
+ # Also report a missing expected PNG file.
+ if not os.path.isfile(expected_png_file):
+ failures.append(test_failures.FailureMissingImage(self))
+
+ # If anything was wrong, write the output files.
+ if len(failures):
+ self.WriteOutputFiles(filename, '', '.checksum', test_args.hash,
+ expected_hash, diff=False)
+ self._CopyOutputPNGs(filename, test_args.png_path,
+ expected_png_file)
+
+ return failures
diff --git a/webkit/tools/layout_tests/test_types/simplified_text_diff.py b/webkit/tools/layout_tests/test_types/simplified_text_diff.py
new file mode 100644
index 0000000..419a7c3
--- /dev/null
+++ b/webkit/tools/layout_tests/test_types/simplified_text_diff.py
@@ -0,0 +1,141 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Compares the text output to the expected text output while ignoring
+positions and sizes of elements/text.
+
+If the output doesn't match, returns FailureSimplifiedTextMismatch and outputs
+the diff files into the layout test results directory.
+"""
+
+import difflib
+import os.path
+import re
+
+from layout_package import test_failures
+from test_types import text_diff
+
+class SimplifiedTextDiff(text_diff.TestTextDiff):
+ def _SimplifyText(self, text):
+ """Removes position and size information from a render tree dump. This
+ also combines contiguous lines of text together so lines that wrap between
+ different words match. Returns the simplified text."""
+
+ # SVG render paths are a little complicated: we want to strip digits after
+ # a decimal point only for strings that begin with "RenderPath.*data".
+ def simplify_svg_path(match):
+ return match.group(1) + re.sub(r"([0-9]*)\.[0-9]{2}", "\\1", match.group(2))
+
+ # Regular expressions to remove or substitue text.
+ simplifications = (
+ # Ignore TypeError and ReferenceError, V8 has different error text.
+ (re.compile(r"Message\tTypeError:.*"), 'Message\tTypeError:'),
+ (re.compile(r"Message\tReferenceError:.*"), 'Message\tReferenceError:'),
+
+ # Ignore uncaught exceptions because V8s error messages are different.
+ (re.compile(r"CONSOLE MESSAGE: line \d+: .*"), 'CONSOLE MESSAGE: line'),
+
+ # Remove position and size information from text.
+ (re.compile(r"at \(-?[0-9]+(\.[0-9]+)?,-?[0-9]+(\.[0-9]+)?\) *"), ''),
+ (re.compile(r"size -?[0-9]+(\.[0-9]+)?x-?[0-9]+(\.[0-9]+)? *"), ''),
+ (re.compile(r' RTL: "\."'), ': ""'),
+ (re.compile(r" (RTL|LTR)( override)?: "), ': '),
+ (re.compile(r"text run width -?[0-9]+: "), ''),
+ (re.compile(r"\([0-9]+px"), ''),
+ (re.compile(r"scrollHeight -?[0-9]+"), ''),
+ (re.compile(r"scrollWidth -?[0-9]+"), ''),
+ (re.compile(r"scrollX -?[0-9]+"), ''),
+ (re.compile(r"scrollY -?[0-9]+"), ''),
+ (re.compile(r"scrolled to [0-9]+,[0-9]+"), 'scrolled to'),
+ (re.compile(r"caret: position -?[0-9]+"), ''),
+
+ # The file select widget has different text on Mac and Win.
+ (re.compile(r"Choose File"), "Browse..."),
+
+ # Remove trailing spaces at the end of a line of text.
+ (re.compile(r' "\n'), '"\n'),
+ # Remove leading spaces at the beginning of a line of text.
+ (re.compile(r'(\s+") '), '\\1'),
+ # Remove empty lines (this only seems to happen with <textareas>)
+ (re.compile(r'^\s*""\n', re.M), ''),
+ # Merge text lines together. Lines ending in anything other than a
+ # hyphen get a space inserted when joined.
+ (re.compile(r'-"\s+"', re.M), '-'),
+ (re.compile(r'"\s+"', re.M), ' '),
+
+ # Handle RTL "...Browse" text. The space gets inserted when text lines
+ # are merged together in the step above.
+ (re.compile(r"... Browse"), "Browse..."),
+
+ # Some SVG tests inexplicably emit -0.00 rather than 0.00 in the expected results
+ (re.compile(r"-0\.00"), '0.00'),
+
+ # Remove size information from SVG text
+ (re.compile(r"(chunk.*width )([0-9]+\.[0-9]{2})"), '\\1'),
+
+ # Remove decimals from SVG paths
+ (re.compile(r"(RenderPath.*data)(.*)"), simplify_svg_path),
+ )
+
+ for regex, subst in simplifications:
+ text = re.sub(regex, subst, text)
+
+ return text
+
+ def CompareOutput(self, filename, proc, output, unused_test_args):
+ """Implementation of CompareOutput that removes most numbers before
+ computing the diff.
+
+ This test does not save new baseline results.
+
+ """
+ failures = []
+
+ # Normalize text to diff
+ output = self.GetNormalizedOutputText(output)
+ expected = self.GetNormalizedExpectedText(filename)
+
+ # Don't bother with the simplified text diff if we match before simplifying
+ # the text.
+ if output == expected:
+ return failures
+
+ # Make the simplified text.
+ output = self._SimplifyText(output)
+ expected = self._SimplifyText(expected)
+
+ if output != expected:
+ # Text doesn't match, write output files.
+ self.WriteOutputFiles(filename, "-simp", ".txt", output, expected)
+
+ # Add failure to return list, unless it's a new test.
+ if expected != '':
+ failures.append(test_failures.FailureSimplifiedTextMismatch(self))
+
+ return failures
diff --git a/webkit/tools/layout_tests/test_types/test_type_base.py b/webkit/tools/layout_tests/test_types/test_type_base.py
new file mode 100644
index 0000000..8afc316
--- /dev/null
+++ b/webkit/tools/layout_tests/test_types/test_type_base.py
@@ -0,0 +1,189 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Defines the interface TestTypeBase which other test types inherit from.
+
+Also defines the TestArguments "struct" to pass them additional arguments.
+"""
+
+import difflib
+import os.path
+
+
+import google.path_utils
+
+from layout_package import path_utils
+from layout_package import platform_utils
+
+class TestArguments(object):
+ """Struct-like wrapper for additional arguments needed by specific tests."""
+ # Outer directory in which to place new baseline results.
+ new_baseline = None
+
+ # Whether to save new text baseline files (otherwise only save image
+ # results as a new baseline).
+ text_baseline = False
+
+ # Path to the actual PNG file generated by pixel tests
+ png_path = None
+
+ # Value of checksum generated by pixel tests.
+ hash = None
+
+
+class TestTypeBase(object):
+ # Filename pieces when writing failures to the test results directory.
+ FILENAME_SUFFIX_ACTUAL = "-actual-win"
+ FILENAME_SUFFIX_EXPECTED = "-expected"
+ FILENAME_SUFFIX_DIFF = "-diff-win"
+
+ def __init__(self, custom_result_id, root_output_dir):
+ """Initialize a TestTypeBase object.
+
+ Args:
+ custom_result_id: the string (generally 'kjs' or 'v8') identifying the
+ custom expected results to be used
+ root_output_dir: The unix style path to the output dir.
+ """
+ self._root_output_dir = root_output_dir
+ self._custom_result_id = custom_result_id
+
+ def _MakeOutputDirectory(self, filename):
+ """Creates the output directory (if needed) for a given test filename."""
+ output_filename = os.path.join(self._root_output_dir,
+ path_utils.RelativeTestFilename(filename))
+ google.path_utils.MaybeMakeDirectory(os.path.split(output_filename)[0])
+
+ def _SaveBaselineData(self, filename, dest_dir, data, modifier):
+ """Saves a new baseline file.
+
+ The file will be named simply "<test>-expected<modifier>", suitable for
+ use as the expected results in a later run.
+
+ Args:
+ filename: the test filename
+ dest_dir: the outer directory into which the results should be saved.
+ The subdirectory corresponding to this test will be created if
+ necessary.
+ data: result to be saved as the new baseline
+ modifier: type of the result file, e.g. ".txt" or ".png"
+ """
+ output_filename = os.path.join(dest_dir,
+ path_utils.RelativeTestFilename(filename))
+ output_filename = (os.path.splitext(output_filename)[0] +
+ self.FILENAME_SUFFIX_EXPECTED + modifier)
+ google.path_utils.MaybeMakeDirectory(os.path.split(output_filename)[0])
+ open(output_filename, "wb").write(data)
+
+ def OutputFilename(self, filename, modifier):
+ """Returns a filename inside the output dir that contains modifier.
+
+ For example, if filename is c:/.../fast/dom/foo.html and modifier is
+ "-expected.txt", the return value is
+ c:/cygwin/tmp/layout-test-results/fast/dom/foo-expected.txt
+
+ Args:
+ filename: absolute filename to test file
+ modifier: a string to replace the extension of filename with
+
+ Return:
+ The absolute windows path to the output filename
+ """
+ output_filename = os.path.join(self._root_output_dir,
+ path_utils.RelativeTestFilename(filename))
+ return os.path.splitext(output_filename)[0] + modifier
+
+ def RelativeOutputFilename(self, filename, modifier):
+ """Returns a relative filename inside the output dir that contains
+ modifier.
+
+ For example, if filename is fast\dom\foo.html and modifier is
+ "-expected.txt", the return value is fast\dom\foo-expected.txt
+
+ Args:
+ filename: relative filename to test file
+ modifier: a string to replace the extension of filename with
+
+ Return:
+ The relative windows path to the output filename
+ """
+ return os.path.splitext(filename)[0] + modifier
+
+ def CompareOutput(self, filename, proc, output, test_args):
+ """Method that compares the output from the test with the expected value.
+
+ This is an abstract method to be implemented by all sub classes.
+
+ Args:
+ filename: absolute filename to test file
+ proc: a reference to the test_shell process
+ output: a string containing the output of the test
+ test_args: a TestArguments object holding optional additional arguments
+
+ Return:
+ a list of TestFailure objects, empty if the test passes
+ """
+ raise NotImplemented
+
+ def WriteOutputFiles(self, filename, test_type, file_type, output, expected,
+ diff=True):
+ """Writes the test output, the expected output and optionally the diff
+ between the two to files in the results directory.
+
+ The full output filename of the actual, for example, will be
+ <filename><test_type>-actual-win<file_type>
+ For instance,
+ my_test-simp-actual-win.txt
+
+ Args:
+ filename: The test filename
+ prefix: A string appended to the test filename, e.g. "-simp". May be "".
+ suffix: A string describing the test output file type, e.g. ".txt"
+ output: A string containing the test output
+ expected: A string containing the expected test output
+ diff: if True, write a file containing the diffs too. This should be
+ False for results that are not text.
+ """
+ self._MakeOutputDirectory(filename)
+ actual_filename = self.OutputFilename(filename,
+ test_type + self.FILENAME_SUFFIX_ACTUAL + file_type)
+ expected_win_filename = self.OutputFilename(filename,
+ test_type + self.FILENAME_SUFFIX_EXPECTED + file_type)
+ open(actual_filename, "wb").write(output)
+ open(expected_win_filename, "wb").write(expected)
+
+ if diff:
+ diff = difflib.unified_diff(expected.splitlines(True),
+ output.splitlines(True),
+ expected_win_filename,
+ actual_filename)
+
+ diff_filename = self.OutputFilename(filename,
+ test_type + self.FILENAME_SUFFIX_DIFF + file_type)
+ open(diff_filename, "wb").write(''.join(diff))
diff --git a/webkit/tools/layout_tests/test_types/text_diff.py b/webkit/tools/layout_tests/test_types/text_diff.py
new file mode 100644
index 0000000..b58fb6a
--- /dev/null
+++ b/webkit/tools/layout_tests/test_types/text_diff.py
@@ -0,0 +1,95 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Compares the text output of a test to the expected text output.
+
+If the output doesn't match, returns FailureTextMismatch and outputs the diff
+files into the layout test results directory.
+"""
+
+import errno
+import os.path
+
+from layout_package import path_utils
+from layout_package import platform_utils
+from layout_package import test_failures
+from test_types import test_type_base
+
+class TestTextDiff(test_type_base.TestTypeBase):
+ def GetNormalizedOutputText(self, output):
+ # Some tests produce "\r\n" explicitly. Our system (Python/Cygwin)
+ # helpfully changes the "\n" to "\r\n", resulting in "\r\r\n".
+ norm = output.replace("\r\r\n", "\r\n").strip("\r\n").replace("\r\n", "\n")
+ return norm + "\n"
+
+ def GetNormalizedExpectedText(self, filename):
+ """Given the filename of the test, read the expected output from a file
+ and normalize the text. Returns a string with the expected text, or ''
+ if the expected output file was not found."""
+ # Read the platform-specific expected text, or the Mac default if no
+ # platform result exists.
+ expected_filename = path_utils.ExpectedFilename(filename, '.txt',
+ self._custom_result_id)
+ try:
+ expected = open(expected_filename).read()
+ except IOError, e:
+ if errno.ENOENT != e.errno:
+ raise
+ expected = ''
+ return expected
+
+ # Normalize line endings
+ return expected.strip("\r\n") + "\n"
+
+ def CompareOutput(self, filename, proc, output, test_args):
+ """Implementation of CompareOutput that checks the output text against the
+ expected text from the LayoutTest directory."""
+ failures = []
+
+ # If we're generating a new baseline, we pass.
+ if test_args.text_baseline:
+ self._SaveBaselineData(filename, test_args.new_baseline,
+ output, ".txt")
+ return failures
+
+ # Normalize text to diff
+ output = self.GetNormalizedOutputText(output)
+ expected = self.GetNormalizedExpectedText(filename)
+
+ # Write output files for new tests, too.
+ if output != expected:
+ # Text doesn't match, write output files.
+ self.WriteOutputFiles(filename, "", ".txt", output, expected)
+
+ if expected == '':
+ failures.append(test_failures.FailureMissingResult(self))
+ else:
+ failures.append(test_failures.FailureTextMismatch(self))
+
+ return failures
diff --git a/webkit/tools/layout_tests/testdata/README b/webkit/tools/layout_tests/testdata/README
new file mode 100644
index 0000000..563e442
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/README
@@ -0,0 +1,2 @@
+This directory contains fake data used by the unittests for
+run_webkit_tests.py.
diff --git a/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-actual-win.txt b/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-actual-win.txt
new file mode 100644
index 0000000..2606bcd
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-actual-win.txt
@@ -0,0 +1,369 @@
+layer at (0,0) size 783x732
+ RenderView at (0,0) size 783x600
+layer at (0,0) size 783x732
+ RenderBlock {HTML} at (0,0) size 783x732
+ RenderBody {BODY} at (8,8) size 767x716
+ RenderBlock {H1} at (0,0) size 767x37
+ RenderText {#text} at (0,0) size 420x36
+ text run at (0,0) width 420: "Form Element Geometry Tests"
+ RenderBlock {P} at (0,58) size 767x20
+ RenderText {#text} at (0,0) size 514x19
+ text run at (0,0) width 514: "These tests help us tune the widget classes in KWQ to have all the right fudge factors."
+ RenderBlock {H2} at (0,97) size 767x27
+ RenderText {#text} at (0,0) size 165x26
+ text run at (0,0) width 165: "Bounding Boxes"
+ RenderTable {TABLE} at (0,143) size 214x47
+ RenderTableSection {TBODY} at (0,0) size 214x47
+ RenderTableRow {TR} at (0,2) size 214x43
+ RenderTableCell {TD} at (2,2) size 84x43 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 82x41 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 78x27
+ RenderButton {INPUT} at (2,2) size 78x37 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 62x28
+ RenderText at (0,0) size 62x27
+ text run at (0,0) width 62: "button"
+ RenderTableCell {TD} at (88,4) size 82x38 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 80x36 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 76x27
+ RenderMenuList {SELECT} at (2,2) size 76x32 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 53x28
+ RenderText at (0,0) size 53x27
+ text run at (0,0) width 53: "menu"
+ RenderBlock (anonymous) at (1,37) size 80x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTableCell {TD} at (172,14) size 19x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 13x13
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderBlock (anonymous) at (1,18) size 17x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTableCell {TD} at (193,14) size 19x19 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 13x13
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderBlock (anonymous) at (1,18) size 17x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTable {TABLE} at (0,190) size 169x39
+ RenderTableSection {TBODY} at (0,0) size 169x39
+ RenderTableRow {TR} at (0,2) size 169x35
+ RenderTableCell {TD} at (2,2) size 60x35 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 58x33 [border: (2px solid #0000FF)]
+ RenderButton {INPUT} at (2,2) size 54x29 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 38x20
+ RenderText at (0,0) size 38x19
+ text run at (0,0) width 38: "button"
+ RenderTableCell {TD} at (64,4) size 61x30 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 59x28 [border: (2px solid #0000FF)]
+ RenderMenuList {SELECT} at (2,2) size 55x24 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 32x20
+ RenderText at (0,0) size 32x19
+ text run at (0,0) width 32: "menu"
+ RenderTableCell {TD} at (127,10) size 19x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderTableCell {TD} at (148,10) size 19x19 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderTable {TABLE} at (0,229) size 147x31
+ RenderTableSection {TBODY} at (0,0) size 147x31
+ RenderTableRow {TR} at (0,2) size 147x27
+ RenderTableCell {TD} at (2,2) size 48x27 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 46x25 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 42x12
+ RenderButton {INPUT} at (2,2) size 42x21 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 26x12
+ RenderText at (0,0) size 26x12
+ text run at (0,0) width 26: "button"
+ RenderTableCell {TD} at (52,4) size 51x22 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 49x20 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 45x12
+ RenderMenuList {SELECT} at (2,2) size 45x16 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 22x12
+ RenderText at (0,0) size 22x12
+ text run at (0,0) width 22: "menu"
+ RenderTableCell {TD} at (105,6) size 19x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 13x10
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderTableCell {TD} at (126,6) size 19x19 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 17x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 13x10
+ RenderBlock {INPUT} at (2,2) size 13x13
+ RenderTable {TABLE} at (0,260) size 622x91
+ RenderTableSection {TBODY} at (0,0) size 622x91
+ RenderTableRow {TR} at (0,2) size 622x87
+ RenderTableCell {TD} at (2,2) size 94x32 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 92x30 [border: (2px solid #0000FF)]
+ RenderTextControl {INPUT} at (2,2) size 88x26 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+ RenderTableCell {TD} at (98,2) size 45x87 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 43x85 [border: (2px solid #0000FF)]
+ RenderListBox {SELECT} at (2,2) size 39x81 [bgcolor=#FFFFFF] [border: (1px inset #808080)]
+ RenderTableCell {TD} at (145,2) size 278x35 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 276x33 [border: (2px solid #0000FF)]
+ RenderFileUploadControl {INPUT} at (2,2) size 272x29
+ RenderButton {INPUT} at (0,0) size 75x29 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (425,2) size 195x52 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 193x50 [border: (2px solid #0000FF)]
+ RenderTextControl {TEXTAREA} at (4,4) size 185x42 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderBlock {H2} at (0,370) size 767x27
+ RenderText {#text} at (0,0) size 199x26
+ text run at (0,0) width 199: "Baseline Alignment"
+ RenderBlock {DIV} at (0,416) size 767x41
+ RenderInline {FONT} at (0,0) size 269x27
+ RenderText {#text} at (0,6) size 43x27
+ text run at (0,6) width 43: "text "
+ RenderButton {INPUT} at (45,2) size 78x37 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 62x28
+ RenderText at (0,0) size 62x27
+ text run at (0,0) width 62: "button"
+ RenderText {#text} at (125,6) size 6x27
+ text run at (125,6) width 6: " "
+ RenderMenuList {SELECT} at (133,4) size 76x32 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 53x28
+ RenderText at (0,0) size 53x27
+ text run at (0,0) width 53: "menu"
+ RenderText {#text} at (211,6) size 6x27
+ text run at (211,6) width 6: " "
+ RenderBlock {INPUT} at (222,14) size 13x13
+ RenderText {#text} at (240,6) size 6x27
+ text run at (240,6) width 6: " "
+ RenderBlock {INPUT} at (251,14) size 13x13
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,457) size 767x33
+ RenderText {#text} at (0,6) size 26x19
+ text run at (0,6) width 26: "text "
+ RenderButton {INPUT} at (28,2) size 54x29 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 38x20
+ RenderText at (0,0) size 38x19
+ text run at (0,0) width 38: "button"
+ RenderText {#text} at (84,6) size 4x19
+ text run at (84,6) width 4: " "
+ RenderMenuList {SELECT} at (90,4) size 55x24 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 32x20
+ RenderText at (0,0) size 32x19
+ text run at (0,0) width 32: "menu"
+ RenderText {#text} at (147,6) size 4x19
+ text run at (147,6) width 4: " "
+ RenderBlock {INPUT} at (155,8) size 13x13
+ RenderText {#text} at (172,6) size 4x19
+ text run at (172,6) width 4: " "
+ RenderBlock {INPUT} at (180,8) size 13x13
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,490) size 767x23
+ RenderInline {FONT} at (0,0) size 148x12
+ RenderText {#text} at (0,6) size 18x12
+ text run at (0,6) width 18: "text "
+ RenderButton {INPUT} at (18,2) size 42x21 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 26x12
+ RenderText at (0,0) size 26x12
+ text run at (0,0) width 26: "button"
+ RenderText {#text} at (60,6) size 3x12
+ text run at (60,6) width 3: " "
+ RenderMenuList {SELECT} at (63,4) size 45x16 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 22x12
+ RenderText at (0,0) size 22x12
+ text run at (0,0) width 22: "menu"
+ RenderText {#text} at (108,6) size 3x12
+ text run at (108,6) width 3: " "
+ RenderBlock {INPUT} at (113,3) size 13x13
+ RenderText {#text} at (128,6) size 3x12
+ text run at (128,6) width 3: " "
+ RenderBlock {INPUT} at (133,3) size 13x13
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,513) size 767x58
+ RenderText {#text} at (0,31) size 26x19
+ text run at (0,31) width 26: "text "
+ RenderTextControl {INPUT} at (28,28) size 88x26 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+ RenderText {#text} at (118,31) size 4x19
+ text run at (118,31) width 4: " "
+ RenderFileUploadControl {INPUT} at (124,27) size 272x29
+ RenderButton {INPUT} at (0,0) size 75x29 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,4) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderText {#text} at (398,31) size 4x19
+ text run at (398,31) width 4: " "
+ RenderTextControl {TEXTAREA} at (404,2) size 185x42 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {H2} at (0,590) size 767x27
+ RenderText {#text} at (0,0) size 195x26
+ text run at (0,0) width 195: "Pop-up Menu Sizes"
+ RenderBlock {DIV} at (0,636) size 767x36
+ RenderInline {FONT} at (0,0) size 194x27
+ RenderText {#text} at (0,0) size 0x0
+ RenderMenuList {SELECT} at (2,2) size 23x32 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 0x28
+ RenderBR at (0,0) size 0x27 [bgcolor=#FFFFFF]
+ RenderText {#text} at (27,4) size 6x27
+ text run at (27,4) width 6: " "
+ RenderMenuList {SELECT} at (35,2) size 28x32 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 5x28
+ RenderText at (0,0) size 5x27
+ text run at (0,0) width 5: "|"
+ RenderText {#text} at (65,4) size 6x27
+ text run at (65,4) width 6: " "
+ RenderMenuList {SELECT} at (73,2) size 119x32 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 96x28
+ RenderText at (0,0) size 96x27
+ text run at (0,0) width 96: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,672) size 767x28
+ RenderMenuList {SELECT} at (2,2) size 23x24 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 0x20
+ RenderBR at (0,0) size 0x19 [bgcolor=#FFFFFF]
+ RenderText {#text} at (27,4) size 4x19
+ text run at (27,4) width 4: " "
+ RenderMenuList {SELECT} at (33,2) size 26x24 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 3x20
+ RenderText at (0,0) size 3x19
+ text run at (0,0) width 3: "|"
+ RenderText {#text} at (61,4) size 4x19
+ text run at (61,4) width 4: " "
+ RenderMenuList {SELECT} at (67,2) size 79x24 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 56x20
+ RenderText at (0,0) size 56x19
+ text run at (0,0) width 56: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,700) size 767x16
+ RenderInline {FONT} at (0,0) size 118x12
+ RenderText {#text} at (0,0) size 0x0
+ RenderMenuList {SELECT} at (0,0) size 23x16 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 0x12
+ RenderBR at (0,0) size 0x12 [bgcolor=#FFFFFF]
+ RenderText {#text} at (23,2) size 3x12
+ text run at (23,2) width 3: " "
+ RenderMenuList {SELECT} at (26,0) size 26x16 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 3x12
+ RenderText at (0,0) size 3x12
+ text run at (0,0) width 3: "|"
+ RenderText {#text} at (52,2) size 3x12
+ text run at (52,2) width 3: " "
+ RenderMenuList {SELECT} at (55,0) size 63x16 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (3,2) size 40x12
+ RenderText at (0,0) size 40x12
+ text run at (0,0) width 40: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+layer at (16,276) size 82x20
+ RenderBlock {DIV} at (3,3) size 82x20
+ RenderText {#text} at (1,0) size 51x19
+ text run at (1,0) width 51: "text field"
+layer at (439,276) size 183x40
+ RenderBlock {DIV} at (1,1) size 183x40
+ RenderText {#text} at (3,0) size 48x19
+ text run at (3,0) width 48: "textarea"
+layer at (39,552) size 82x20
+ RenderBlock {DIV} at (3,3) size 82x20
+ RenderText {#text} at (1,0) size 51x19
+ text run at (1,0) width 51: "text field"
+layer at (413,524) size 183x40
+ RenderBlock {DIV} at (1,1) size 183x40
+ RenderText {#text} at (3,0) size 48x19
+ text run at (3,0) width 48: "textarea"
+layer at (0,0) size 1220x583
+ RenderView at (0,0) size 800x583
+layer at (0,0) size 1220x583
+ RenderBlock {HTML} at (0,0) size 800x583
+ RenderBody {BODY} at (8,8) size 784x567
+ RenderTable {TABLE} at (0,0) size 1212x131
+ RenderTableSection {TBODY} at (0,0) size 1212x131
+ RenderTableRow {TR} at (0,2) size 1212x22
+ RenderTableCell {TH} at (2,12) size 80x2 [r=0 c=0 rs=1 cs=1]
+ RenderTableCell {TH} at (84,12) size 280x2 [r=0 c=1 rs=1 cs=1]
+ RenderTableCell {TH} at (366,2) size 280x22 [r=0 c=2 rs=1 cs=1]
+ RenderText {#text} at (95,1) size 90x19
+ text run at (95,1) width 90: "text-align:left"
+ RenderTableCell {TH} at (648,2) size 280x22 [r=0 c=3 rs=1 cs=1]
+ RenderText {#text} at (85,1) size 110x19
+ text run at (85,1) width 110: "text-align:center"
+ RenderTableCell {TH} at (930,2) size 280x22 [r=0 c=4 rs=1 cs=1]
+ RenderText {#text} at (90,1) size 99x19
+ text run at (90,1) width 99: "text-align:right"
+ RenderTableRow {TR} at (0,26) size 1212x33
+ RenderTableCell {TH} at (2,41) size 80x2 [r=1 c=0 rs=1 cs=1]
+ RenderTableCell {TD} at (84,26) size 280x33 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (366,26) size 280x33 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (648,26) size 280x33 [border: (1px solid #000000)] [r=1 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (930,26) size 280x33 [border: (1px solid #000000)] [r=1 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableRow {TR} at (0,61) size 1212x33
+ RenderTableCell {TH} at (2,66) size 80x22 [r=2 c=0 rs=1 cs=1]
+ RenderText {#text} at (1,1) size 78x19
+ text run at (1,1) width 78: "direction:ltr"
+ RenderTableCell {TD} at (84,61) size 280x33 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (366,61) size 280x33 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (648,61) size 280x33 [border: (1px solid #000000)] [r=2 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableCell {TD} at (930,61) size 280x33 [border: (1px solid #000000)] [r=2 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (0,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 59: "Browse..."
+ RenderTableRow {TR} at (0,96) size 1212x33
+ RenderTableCell {TH} at (2,101) size 80x22 [r=3 c=0 rs=1 cs=1]
+ RenderText {#text} at (1,1) size 78x19
+ text run at (1,1) width 78: "direction:rtl"
+ RenderTableCell {TD} at (84,96) size 280x33 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (201,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 12 RTL: "..."
+ text run at (12,0) width 47: "Browse"
+ RenderTableCell {TD} at (366,96) size 280x33 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (201,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 12 RTL: "..."
+ text run at (12,0) width 47: "Browse"
+ RenderTableCell {TD} at (648,96) size 280x33 [border: (1px solid #000000)] [r=3 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (201,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 12 RTL: "..."
+ text run at (12,0) width 47: "Browse"
+ RenderTableCell {TD} at (930,96) size 280x33 [border: (1px solid #000000)] [r=3 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 272x25
+ RenderButton {INPUT} at (201,0) size 71x25 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (6,2) size 59x20
+ RenderText at (0,0) size 59x19
+ text run at (0,0) width 12 RTL: "..."
+ text run at (12,0) width 47: "Browse"
diff --git a/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-expected.txt b/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-expected.txt
new file mode 100644
index 0000000..e421005
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/form-element-geometry-expected.txt
@@ -0,0 +1,365 @@
+layer at (0,0) size 785x626
+ RenderView at (0,0) size 785x600
+layer at (0,0) size 785x626
+ RenderBlock {HTML} at (0,0) size 785x626
+ RenderBody {BODY} at (8,8) size 769x610
+ RenderBlock {H1} at (0,0) size 769x37
+ RenderText {#text} at (0,0) size 422x37
+ text run at (0,0) width 422: "Form Element Geometry Tests"
+ RenderBlock {P} at (0,58) size 769x18
+ RenderText {#text} at (0,0) size 540x18
+ text run at (0,0) width 540: "These tests help us tune the widget classes in KWQ to have all the right fudge factors."
+ RenderBlock {H2} at (0,95) size 769x28
+ RenderText {#text} at (0,0) size 165x28
+ text run at (0,0) width 165: "Bounding Boxes"
+ RenderTable {TABLE} at (0,142) size 172x28
+ RenderTableSection {TBODY} at (0,0) size 172x28
+ RenderTableRow {TR} at (0,2) size 172x24
+ RenderTableCell {TD} at (2,2) size 58x24 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 56x22 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 52x18
+ RenderButton {INPUT} at (2,2) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderTableCell {TD} at (62,2) size 68x24 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 66x22 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 62x18
+ RenderMenuList {SELECT} at (2,2) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderBlock (anonymous) at (1,23) size 66x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTableCell {TD} at (132,4) size 18x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 12x13
+ RenderBlock {INPUT} at (2,2) size 12x13
+ RenderBlock (anonymous) at (1,18) size 16x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTableCell {TD} at (152,5) size 18x18 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x16 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 12x12
+ RenderBlock {INPUT} at (2,2) size 12x12
+ RenderBlock (anonymous) at (1,17) size 16x0
+ RenderInline {FONT} at (0,0) size 0x0
+ RenderTable {TABLE} at (0,170) size 172x28
+ RenderTableSection {TBODY} at (0,0) size 172x28
+ RenderTableRow {TR} at (0,2) size 172x24
+ RenderTableCell {TD} at (2,2) size 58x24 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 56x22 [border: (2px solid #0000FF)]
+ RenderButton {INPUT} at (2,2) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderTableCell {TD} at (62,2) size 68x24 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 66x22 [border: (2px solid #0000FF)]
+ RenderMenuList {SELECT} at (2,2) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderTableCell {TD} at (132,4) size 18x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x17 [border: (2px solid #0000FF)]
+ RenderBlock {INPUT} at (2,2) size 12x13
+ RenderTableCell {TD} at (152,5) size 18x18 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x16 [border: (2px solid #0000FF)]
+ RenderBlock {INPUT} at (2,2) size 12x12
+ RenderTable {TABLE} at (0,198) size 172x28
+ RenderTableSection {TBODY} at (0,0) size 172x28
+ RenderTableRow {TR} at (0,2) size 172x24
+ RenderTableCell {TD} at (2,2) size 58x24 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 56x22 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 52x13
+ RenderButton {INPUT} at (2,2) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderTableCell {TD} at (62,2) size 68x24 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 66x22 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 62x13
+ RenderMenuList {SELECT} at (2,2) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderTableCell {TD} at (132,4) size 18x19 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x17 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 12x12
+ RenderBlock {INPUT} at (2,2) size 12x13
+ RenderTableCell {TD} at (152,5) size 18x18 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 16x16 [border: (2px solid #0000FF)]
+ RenderInline {FONT} at (0,0) size 12x12
+ RenderBlock {INPUT} at (2,2) size 12x12
+ RenderTable {TABLE} at (0,226) size 550x67
+ RenderTableSection {TBODY} at (0,0) size 550x67
+ RenderTableRow {TR} at (0,2) size 550x63
+ RenderTableCell {TD} at (2,2) size 84x25 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 82x23 [border: (2px solid #0000FF)]
+ RenderTextControl {INPUT} at (2,2) size 78x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+ RenderTableCell {TD} at (88,2) size 40x63 [r=0 c=1 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 38x61 [border: (2px solid #0000FF)]
+ RenderListBox {SELECT} at (2,2) size 34x57 [bgcolor=#FFFFFF] [border: (1px inset #808080)]
+ RenderTableCell {TD} at (130,2) size 243x24 [r=0 c=2 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 241x22 [border: (2px solid #0000FF)]
+ RenderFileUploadControl {INPUT} at (2,2) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (375,2) size 173x38 [r=0 c=3 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 171x36 [border: (2px solid #0000FF)]
+ RenderTextControl {TEXTAREA} at (4,4) size 163x28 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderBlock {H2} at (0,312) size 769x28
+ RenderText {#text} at (0,0) size 200x28
+ text run at (0,0) width 200: "Baseline Alignment"
+ RenderBlock {DIV} at (0,359) size 769x29
+ RenderInline {FONT} at (0,0) size 219x28
+ RenderText {#text} at (0,0) size 43x28
+ text run at (0,0) width 43: "text "
+ RenderButton {INPUT} at (45,9) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderText {#text} at (99,0) size 6x28
+ text run at (99,0) width 6: " "
+ RenderMenuList {SELECT} at (107,9) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderText {#text} at (171,0) size 6x28
+ text run at (171,0) width 6: " "
+ RenderBlock {INPUT} at (180,11) size 12x13
+ RenderText {#text} at (195,0) size 6x28
+ text run at (195,0) width 6: " "
+ RenderBlock {INPUT} at (204,12) size 12x12
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,388) size 769x22
+ RenderText {#text} at (0,1) size 27x18
+ text run at (0,1) width 27: "text "
+ RenderButton {INPUT} at (29,2) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderText {#text} at (83,1) size 4x18
+ text run at (83,1) width 4: " "
+ RenderMenuList {SELECT} at (89,2) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderText {#text} at (153,1) size 4x18
+ text run at (153,1) width 4: " "
+ RenderBlock {INPUT} at (160,4) size 12x13
+ RenderText {#text} at (175,1) size 4x18
+ text run at (175,1) width 4: " "
+ RenderBlock {INPUT} at (182,5) size 12x12
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,410) size 769x22
+ RenderInline {FONT} at (0,0) size 185x13
+ RenderText {#text} at (0,5) size 18x13
+ text run at (0,5) width 18: "text "
+ RenderButton {INPUT} at (20,2) size 52x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 36x13
+ RenderText at (0,0) size 36x13
+ text run at (0,0) width 36: "button"
+ RenderText {#text} at (74,5) size 3x13
+ text run at (74,5) width 3: " "
+ RenderMenuList {SELECT} at (79,2) size 62x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 31x13
+ RenderText at (0,0) size 31x13
+ text run at (0,0) width 31: "menu"
+ RenderText {#text} at (143,5) size 3x13
+ text run at (143,5) width 3: " "
+ RenderBlock {INPUT} at (149,4) size 12x13
+ RenderText {#text} at (164,5) size 3x13
+ text run at (164,5) width 3: " "
+ RenderBlock {INPUT} at (170,5) size 12x12
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,432) size 769x39
+ RenderText {#text} at (0,18) size 27x18
+ text run at (0,18) width 27: "text "
+ RenderTextControl {INPUT} at (29,18) size 78x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+ RenderText {#text} at (109,18) size 4x18
+ text run at (109,18) width 4: " "
+ RenderFileUploadControl {INPUT} at (115,19) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderText {#text} at (354,18) size 4x18
+ text run at (354,18) width 4: " "
+ RenderTextControl {TEXTAREA} at (360,2) size 163x28 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {H2} at (0,490) size 769x28
+ RenderText {#text} at (0,0) size 197x28
+ text run at (0,0) width 197: "Pop-up Menu Sizes"
+ RenderBlock {DIV} at (0,537) size 769x29
+ RenderInline {FONT} at (0,0) size 181x28
+ RenderText {#text} at (0,0) size 0x0
+ RenderMenuList {SELECT} at (2,9) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderBR at (0,0) size 0x13 [bgcolor=#FFFFFF]
+ RenderText {#text} at (40,0) size 6x28
+ text run at (40,0) width 6: " "
+ RenderMenuList {SELECT} at (48,9) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderText at (0,0) size 5x13
+ text run at (0,0) width 5: "|"
+ RenderText {#text} at (86,0) size 6x28
+ text run at (86,0) width 6: " "
+ RenderMenuList {SELECT} at (94,9) size 85x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 54x13
+ RenderText at (0,0) size 54x13
+ text run at (0,0) width 54: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,566) size 769x22
+ RenderMenuList {SELECT} at (2,2) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderBR at (0,0) size 0x13 [bgcolor=#FFFFFF]
+ RenderText {#text} at (40,1) size 4x18
+ text run at (40,1) width 4: " "
+ RenderMenuList {SELECT} at (46,2) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderText at (0,0) size 5x13
+ text run at (0,0) width 5: "|"
+ RenderText {#text} at (84,1) size 4x18
+ text run at (84,1) width 4: " "
+ RenderMenuList {SELECT} at (90,2) size 85x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 54x13
+ RenderText at (0,0) size 54x13
+ text run at (0,0) width 54: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,588) size 769x22
+ RenderInline {FONT} at (0,0) size 175x13
+ RenderText {#text} at (0,0) size 0x0
+ RenderMenuList {SELECT} at (2,2) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderBR at (0,0) size 0x13 [bgcolor=#FFFFFF]
+ RenderText {#text} at (40,5) size 3x13
+ text run at (40,5) width 3: " "
+ RenderMenuList {SELECT} at (45,2) size 36x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 5x13
+ RenderText at (0,0) size 5x13
+ text run at (0,0) width 5: "|"
+ RenderText {#text} at (83,5) size 3x13
+ text run at (83,5) width 3: " "
+ RenderMenuList {SELECT} at (88,2) size 85x18 [bgcolor=#FFFFFF]
+ RenderBlock (anonymous) at (8,2) size 54x13
+ RenderText at (0,0) size 54x13
+ text run at (0,0) width 54: "xxxxxxxx"
+ RenderText {#text} at (0,0) size 0x0
+layer at (16,242) size 72x13
+ RenderBlock {DIV} at (3,3) size 72x13
+ RenderText {#text} at (1,0) size 49x13
+ text run at (1,0) width 49: "text field"
+layer at (389,242) size 161x26
+ RenderBlock {DIV} at (1,1) size 161x26
+ RenderText {#text} at (3,0) size 44x13
+ text run at (3,0) width 44: "textarea"
+layer at (40,461) size 72x13
+ RenderBlock {DIV} at (3,3) size 72x13
+ RenderText {#text} at (1,0) size 49x13
+ text run at (1,0) width 49: "text field"
+layer at (369,443) size 161x26
+ RenderBlock {DIV} at (1,1) size 161x26
+ RenderText {#text} at (3,0) size 44x13
+ text run at (3,0) width 44: "textarea"
+layer at (0,0) size 1083x585
+ RenderView at (0,0) size 800x585
+layer at (0,0) size 1083x585
+ RenderBlock {HTML} at (0,0) size 800x585
+ RenderBody {BODY} at (8,8) size 784x569
+ RenderTable {TABLE} at (0,0) size 1075x108
+ RenderTableSection {TBODY} at (0,0) size 1075x108
+ RenderTableRow {TR} at (0,2) size 1075x20
+ RenderTableCell {TH} at (2,11) size 83x2 [r=0 c=0 rs=1 cs=1]
+ RenderTableCell {TH} at (87,11) size 245x2 [r=0 c=1 rs=1 cs=1]
+ RenderTableCell {TH} at (334,2) size 245x20 [r=0 c=2 rs=1 cs=1]
+ RenderText {#text} at (78,1) size 89x18
+ text run at (78,1) width 89: "text-align:left"
+ RenderTableCell {TH} at (581,2) size 245x20 [r=0 c=3 rs=1 cs=1]
+ RenderText {#text} at (67,1) size 110x18
+ text run at (67,1) width 110: "text-align:center"
+ RenderTableCell {TH} at (828,2) size 245x20 [r=0 c=4 rs=1 cs=1]
+ RenderText {#text} at (72,1) size 101x18
+ text run at (72,1) width 101: "text-align:right"
+ RenderTableRow {TR} at (0,24) size 1075x26
+ RenderTableCell {TH} at (2,36) size 83x2 [r=1 c=0 rs=1 cs=1]
+ RenderTableCell {TD} at (87,24) size 245x26 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (334,24) size 245x26 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (581,24) size 245x26 [border: (1px solid #000000)] [r=1 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (828,24) size 245x26 [border: (1px solid #000000)] [r=1 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableRow {TR} at (0,52) size 1075x26
+ RenderTableCell {TH} at (2,55) size 83x20 [r=2 c=0 rs=1 cs=1]
+ RenderText {#text} at (1,1) size 81x18
+ text run at (1,1) width 81: "direction:ltr"
+ RenderTableCell {TD} at (87,52) size 245x26 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (334,52) size 245x26 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (581,52) size 245x26 [border: (1px solid #000000)] [r=2 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (828,52) size 245x26 [border: (1px solid #000000)] [r=2 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (0,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableRow {TR} at (0,80) size 1075x26
+ RenderTableCell {TH} at (2,83) size 83x20 [r=3 c=0 rs=1 cs=1]
+ RenderText {#text} at (1,1) size 81x18
+ text run at (1,1) width 81: "direction:rtl"
+ RenderTableCell {TD} at (87,80) size 245x26 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (159,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (334,80) size 245x26 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (159,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (581,80) size 245x26 [border: (1px solid #000000)] [r=3 c=3 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (159,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
+ RenderTableCell {TD} at (828,80) size 245x26 [border: (1px solid #000000)] [r=3 c=4 rs=1 cs=1]
+ RenderFileUploadControl {INPUT} at (4,4) size 237x18
+ RenderButton {INPUT} at (159,0) size 78x18 [bgcolor=#C0C0C0]
+ RenderBlock (anonymous) at (8,2) size 62x13
+ RenderText at (0,0) size 62x13
+ text run at (0,0) width 62: "Choose File"
diff --git a/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-actual-win.txt b/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-actual-win.txt
new file mode 100644
index 0000000..d905358
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-actual-win.txt
@@ -0,0 +1,82 @@
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderText {#text} at (0,0) size 763x39
+ text run at (0,0) width 763: "This test verifies that JS access to offsetParent on an element that lacks one, such as the body, doesn't crash. If it didn't crash, it"
+ text run at (0,20) width 46: "passed."
+ RenderText {#text} at (0,0) size 0x0
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderBlock {P} at (0,0) size 784x20
+ RenderText {#text} at (0,0) size 107x19
+ text run at (0,0) width 107: "Tests: the bdo tag"
+ RenderBlock {P} at (0,36) size 784x20
+ RenderText {#text} at (0,0) size 313x19
+ text run at (0,0) width 313: "The bdo element overrides the default text direction."
+ RenderBlock {P} at (0,72) size 784x40
+ RenderText {#text} at (0,0) size 757x39
+ text run at (0,0) width 696: "If successful, the first sentence would be backward, and the second sentence regular. There should then be an extra "
+ text run at (696,0) width 61: "blank line,"
+ text run at (0,20) width 622: "followed by a line reading only \"A,\" and finally, a sentence where only the word \"umbrella\" is backward."
+ RenderBlock {HR} at (0,128) size 784x2 [border: (1px inset #000000)]
+ RenderBlock (anonymous) at (0,138) size 784x120
+ RenderBR {BR} at (0,0) size 0x19
+ RenderInline {BDO} at (0,0) size 212x19
+ RenderText {#text} at (0,20) size 212x19
+ text run at (0,20) width 212 RTL override: "This sentence should be backward."
+ RenderText {#text} at (212,20) size 4x19
+ text run at (212,20) width 4: " "
+ RenderBR {BR} at (216,35) size 0x0
+ RenderInline {BDO} at (0,0) size 199x19
+ RenderText {#text} at (0,40) size 199x19
+ text run at (0,40) width 199 LTR override: "This sentence should be forward."
+ RenderText {#text} at (199,40) size 4x19
+ text run at (199,40) width 4: " "
+ RenderBR {BR} at (203,55) size 0x0
+ RenderInline {BDO} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBR {BR} at (0,60) size 0x19
+ RenderInline {BDO} at (0,0) size 11x19
+ RenderText {#text} at (0,80) size 11x19
+ text run at (0,80) width 11 RTL override: "A"
+ RenderText {#text} at (11,80) size 4x19
+ text run at (11,80) width 4: " "
+ RenderBR {BR} at (15,95) size 0x0
+ RenderInline {BDO} at (0,0) size 271x19
+ RenderText {#text} at (0,100) size 25x19
+ text run at (0,100) width 25 LTR override: "My "
+ RenderInline {BDO} at (0,0) size 51x19
+ RenderText {#text} at (25,100) size 51x19
+ text run at (25,100) width 51 RTL override: "umbrella"
+ RenderText {#text} at (76,100) size 195x19
+ text run at (76,100) width 195 LTR override: " sure would be useful in this rain."
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x576
+ RenderBlock {P} at (0,0) size 784x40
+ RenderText {#text} at (0,0) size 242x19
+ text run at (0,0) width 242: "This test checks for a regression against "
+ RenderInline {I} at (0,0) size 752x39
+ RenderInline {A} at (0,0) size 350x19 [color=#0000EE]
+ RenderText {#text} at (242,0) size 350x19
+ text run at (242,0) width 350: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6214"
+ RenderText {#text} at (592,0) size 752x39
+ text run at (592,0) width 160: " text-indent in RTL block"
+ text run at (0,20) width 111: "does the opposite"
+ RenderText {#text} at (111,20) size 4x19
+ text run at (111,20) width 4: "."
+ RenderBlock {HR} at (0,56) size 784x2 [border: (1px inset #000000)]
+ RenderBlock {P} at (0,74) size 784x40 [bgcolor=#00FFFF]
+ RenderText {#text} at (5,0) size 779x39
+ text run at (5,0) width 729: "The first line of this sententce should be indented 50 pixels to the left, but the rest of it should be flush with the normal right"
+ text run at (669,20) width 4 RTL: "."
+ text run at (673,20) width 111: "margin of the page"
diff --git a/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-expected.txt b/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-expected.txt
new file mode 100644
index 0000000..0afa5c7
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/null-offset-parent-expected.txt
@@ -0,0 +1,82 @@
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderText {#text} at (0,0) size 782x36
+ text run at (0,0) width 782: "This test verifies that JS access to offsetParent on an element that lacks one, such as the body, doesn't crash. If it didn't crash,"
+ text run at (0,18) width 58: "it passed."
+ RenderText {#text} at (0,0) size 0x0
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderBlock {P} at (0,0) size 784x18
+ RenderText {#text} at (0,0) size 111x18
+ text run at (0,0) width 111: "Tests: the bdo tag"
+ RenderBlock {P} at (0,34) size 784x18
+ RenderText {#text} at (0,0) size 328x18
+ text run at (0,0) width 328: "The bdo element overrides the default text direction."
+ RenderBlock {P} at (0,68) size 784x36
+ RenderText {#text} at (0,0) size 762x36
+ text run at (0,0) width 727: "If successful, the first sentence would be backward, and the second sentence regular. There should then be an extra "
+ text run at (727,0) width 35: "blank"
+ text run at (0,18) width 698: "line, followed by a line reading only \"A,\" and finally, a sentence where only the word \"umbrella\" is backward."
+ RenderBlock {HR} at (0,120) size 784x2 [border: (1px inset #000000)]
+ RenderBlock (anonymous) at (0,130) size 784x108
+ RenderBR {BR} at (0,0) size 0x18
+ RenderInline {BDO} at (0,0) size 221x18
+ RenderText {#text} at (0,18) size 221x18
+ text run at (0,18) width 221 RTL override: "This sentence should be backward."
+ RenderText {#text} at (221,18) size 4x18
+ text run at (221,18) width 4: " "
+ RenderBR {BR} at (225,32) size 0x0
+ RenderInline {BDO} at (0,0) size 209x18
+ RenderText {#text} at (0,36) size 209x18
+ text run at (0,36) width 209 LTR override: "This sentence should be forward."
+ RenderText {#text} at (209,36) size 4x18
+ text run at (209,36) width 4: " "
+ RenderBR {BR} at (213,50) size 0x0
+ RenderInline {BDO} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBR {BR} at (0,54) size 0x18
+ RenderInline {BDO} at (0,0) size 12x18
+ RenderText {#text} at (0,72) size 12x18
+ text run at (0,72) width 12 RTL override: "A"
+ RenderText {#text} at (12,72) size 4x18
+ text run at (12,72) width 4: " "
+ RenderBR {BR} at (16,86) size 0x0
+ RenderInline {BDO} at (0,0) size 290x18
+ RenderText {#text} at (0,90) size 26x18
+ text run at (0,90) width 26 LTR override: "My "
+ RenderInline {BDO} at (0,0) size 55x18
+ RenderText {#text} at (26,90) size 55x18
+ text run at (26,90) width 55 RTL override: "umbrella"
+ RenderText {#text} at (81,90) size 209x18
+ text run at (81,90) width 209 LTR override: " sure would be useful in this rain."
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x576
+ RenderBlock {P} at (0,0) size 784x36
+ RenderText {#text} at (0,0) size 253x18
+ text run at (0,0) width 253: "This test checks for a regression against "
+ RenderInline {I} at (0,0) size 757x36
+ RenderInline {A} at (0,0) size 348x18 [color=#0000EE]
+ RenderText {#text} at (253,0) size 348x18
+ text run at (253,0) width 348: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6214"
+ RenderText {#text} at (601,0) size 757x36
+ text run at (601,0) width 156: " text-indent in RTL block"
+ text run at (0,18) width 109: "does the opposite"
+ RenderText {#text} at (109,18) size 4x18
+ text run at (109,18) width 4: "."
+ RenderBlock {HR} at (0,52) size 784x2 [border: (1px inset #000000)]
+ RenderBlock {P} at (0,70) size 784x36 [bgcolor=#00FFFF]
+ RenderText {#text} at (41,0) size 743x36
+ text run at (41,0) width 693: "The first line of this sententce should be indented 50 pixels to the left, but the rest of it should be flush with the"
+ text run at (581,18) width 4 RTL: "."
+ text run at (585,18) width 199: "normal right margin of the page"
diff --git a/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-actual-win.txt b/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-actual-win.txt
new file mode 100644
index 0000000..fbdb37f
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-actual-win.txt
@@ -0,0 +1,75 @@
+layer at (0,0) size 783x1249
+ RenderView at (0,0) size 783x600
+layer at (0,0) size 783x1249
+ RenderBlock {HTML} at (0,0) size 783x1249
+ RenderBody {BODY} at (8,8) size 767x1225
+ RenderBlock (anonymous) at (0,0) size 767x20
+ RenderText {#text} at (0,0) size 254x19
+ text run at (0,0) width 254: "line-height settings not reflected in textarea"
+ RenderBR {BR} at (254,0) size 0x19
+ RenderBlock {P} at (0,36) size 767x267
+ RenderText {#text} at (0,0) size 79x19
+ text run at (0,0) width 79: "TEXTAREA"
+ RenderBR {BR} at (79,0) size 0x19
+ RenderTextControl {TEXTAREA} at (0,20) size 402x202 [bgcolor=#FFFFFF] [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (402,207) size 4x19
+ text run at (402,207) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,227) size 0x19
+ RenderText {#text} at (0,247) size 145x19
+ text run at (0,247) width 145: "PARAGRAPH - works"
+ RenderBlock {P} at (0,319) size 402x202 [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (1,19) size 385x68
+ text run at (1,19) width 385: "Demo text here that wraps a bit and should demonstrate"
+ text run at (1,71) width 181: "the goodness of line-height"
+ RenderBlock (anonymous) at (0,534) size 767x40
+ RenderBR {BR} at (0,0) size 0x19
+ RenderText {#text} at (0,20) size 79x19
+ text run at (0,20) width 79: "DIV - works"
+ RenderBR {BR} at (79,20) size 0x19
+ RenderBlock {DIV} at (0,574) size 402x202 [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (1,19) size 385x68
+ text run at (1,19) width 385: "Demo text here that wraps a bit and should demonstrate"
+ text run at (1,71) width 181: "the goodness of line-height"
+ RenderBlock (anonymous) at (0,776) size 767x449
+ RenderBR {BR} at (0,0) size 0x19
+ RenderBR {BR} at (0,20) size 0x19
+ RenderText {#text} at (0,40) size 119x19
+ text run at (0,40) width 119: "Un-Styled Textarea"
+ RenderBR {BR} at (119,40) size 0x19
+ RenderTextControl {TEXTAREA} at (2,62) size 183x42 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (187,91) size 4x19
+ text run at (187,91) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,111) size 0x19
+ RenderText {#text} at (0,131) size 203x19
+ text run at (0,131) width 203: "Totally Blank Un-Styled Textarea"
+ RenderBR {BR} at (203,131) size 0x19
+ RenderTextControl {TEXTAREA} at (2,153) size 183x42 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (187,182) size 4x19
+ text run at (187,182) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,202) size 0x19
+ RenderText {#text} at (0,222) size 199x19
+ text run at (0,222) width 199: "Totally Blank STYLED Textarea"
+ RenderBR {BR} at (199,222) size 0x19
+ RenderTextControl {TEXTAREA} at (0,242) size 402x202 [bgcolor=#FFFFFF] [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {P} at (0,1241) size 767x0
+layer at (9,65) size 400x200
+ RenderBlock {DIV} at (1,1) size 400x200
+ RenderText {#text} at (3,18) size 390x68
+ text run at (3,18) width 390: "Demo text here that wraps a bit and should demonstrate "
+ text run at (3,70) width 181: "the goodness of line-height"
+layer at (11,847) size 181x40 clip at (11,847) size 164x40 scrollHeight 79
+ RenderBlock {DIV} at (1,1) size 181x40
+ RenderText {#text} at (3,0) size 158x79
+ text run at (3,0) width 123: "Demo text here that "
+ text run at (3,20) width 140: "wraps a bit and should "
+ text run at (3,40) width 157: "demonstrate the goodness"
+ text run at (160,40) width 1: " "
+ text run at (3,60) width 77: "of line-height"
+layer at (11,938) size 181x40
+ RenderBlock {DIV} at (1,1) size 181x40
+layer at (9,1027) size 400x200
+ RenderBlock {DIV} at (1,1) size 400x200
diff --git a/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-expected.txt b/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-expected.txt
new file mode 100644
index 0000000..1de6aae
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/difftests/textAreaLineHeight-expected.txt
@@ -0,0 +1,74 @@
+layer at (0,0) size 785x1191
+ RenderView at (0,0) size 785x600
+layer at (0,0) size 785x1191
+ RenderBlock {HTML} at (0,0) size 785x1191
+ RenderBody {BODY} at (8,8) size 769x1167
+ RenderBlock (anonymous) at (0,0) size 769x18
+ RenderText {#text} at (0,0) size 269x18
+ text run at (0,0) width 269: "line-height settings not reflected in textarea"
+ RenderBR {BR} at (269,0) size 0x18
+ RenderBlock {P} at (0,34) size 769x260
+ RenderText {#text} at (0,0) size 87x18
+ text run at (0,0) width 87: "TEXTAREA"
+ RenderBR {BR} at (87,0) size 0x18
+ RenderTextControl {TEXTAREA} at (0,18) size 402x202 [bgcolor=#FFFFFF] [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (402,206) size 4x18
+ text run at (402,206) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,224) size 0x18
+ RenderText {#text} at (0,242) size 152x18
+ text run at (0,242) width 152: "PARAGRAPH - works"
+ RenderBlock {P} at (0,310) size 402x202 [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (1,19) size 382x68
+ text run at (1,19) width 382: "Demo text here that wraps a bit and should demonstrate"
+ text run at (1,71) width 182: "the goodness of line-height"
+ RenderBlock (anonymous) at (0,525) size 769x36
+ RenderBR {BR} at (0,0) size 0x18
+ RenderText {#text} at (0,18) size 81x18
+ text run at (0,18) width 81: "DIV - works"
+ RenderBR {BR} at (81,18) size 0x18
+ RenderBlock {DIV} at (0,561) size 402x202 [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (1,19) size 382x68
+ text run at (1,19) width 382: "Demo text here that wraps a bit and should demonstrate"
+ text run at (1,71) width 182: "the goodness of line-height"
+ RenderBlock (anonymous) at (0,763) size 769x404
+ RenderBR {BR} at (0,0) size 0x18
+ RenderBR {BR} at (0,18) size 0x18
+ RenderText {#text} at (0,36) size 124x18
+ text run at (0,36) width 124: "Un-Styled Textarea"
+ RenderBR {BR} at (124,36) size 0x18
+ RenderTextControl {TEXTAREA} at (2,56) size 163x28 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (167,72) size 4x18
+ text run at (167,72) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,90) size 0x18
+ RenderText {#text} at (0,108) size 215x18
+ text run at (0,108) width 215: "Totally Blank Un-Styled Textarea"
+ RenderBR {BR} at (215,108) size 0x18
+ RenderTextControl {TEXTAREA} at (2,128) size 163x28 [bgcolor=#FFFFFF] [border: (1px solid #000000)]
+ RenderText {#text} at (167,144) size 4x18
+ text run at (167,144) width 4: " "
+ RenderBR {BR} at (0,0) size 0x0
+ RenderBR {BR} at (0,162) size 0x18
+ RenderText {#text} at (0,180) size 213x18
+ text run at (0,180) width 213: "Totally Blank STYLED Textarea"
+ RenderBR {BR} at (213,180) size 0x18
+ RenderTextControl {TEXTAREA} at (0,198) size 402x202 [bgcolor=#FFFFFF] [border: (1px dotted #C0C0C0)]
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {P} at (0,1183) size 769x0
+layer at (9,61) size 400x200
+ RenderBlock {DIV} at (1,1) size 400x200
+ RenderText {#text} at (3,18) size 387x68
+ text run at (3,18) width 387: "Demo text here that wraps a bit and should demonstrate "
+ text run at (3,70) width 182: "the goodness of line-height"
+layer at (11,828) size 161x26 clip at (11,828) size 146x26 scrollHeight 52
+ RenderBlock {DIV} at (1,1) size 161x26
+ RenderText {#text} at (3,0) size 130x52
+ text run at (3,0) width 112: "Demo text here that "
+ text run at (3,13) width 126: "wraps a bit and should "
+ text run at (3,26) width 92: "demonstrate the "
+ text run at (3,39) width 130: "goodness of line-height"
+layer at (11,900) size 161x26
+ RenderBlock {DIV} at (1,1) size 161x26
+layer at (9,970) size 400x200
+ RenderBlock {DIV} at (1,1) size 400x200
diff --git a/webkit/tools/layout_tests/testdata/expected-crashes-new-passing.txt b/webkit/tools/layout_tests/testdata/expected-crashes-new-passing.txt
new file mode 100644
index 0000000..97ec414
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-crashes-new-passing.txt
@@ -0,0 +1,2 @@
+fast/bar/crash2.html
+fast/foo/crash1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-crashes-new-test.txt b/webkit/tools/layout_tests/testdata/expected-crashes-new-test.txt
new file mode 100644
index 0000000..d6d3da2
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-crashes-new-test.txt
@@ -0,0 +1,4 @@
+fast/bar/betz/crash3.html
+fast/bar/crash2.html
+fast/foo/crash1.html
+new-test.html
diff --git a/webkit/tools/layout_tests/testdata/expected-crashes.txt b/webkit/tools/layout_tests/testdata/expected-crashes.txt
new file mode 100644
index 0000000..ca65a65
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-crashes.txt
@@ -0,0 +1,3 @@
+fast/bar/betz/crash3.html
+fast/bar/crash2.html
+fast/foo/crash1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-failures-added.txt b/webkit/tools/layout_tests/testdata/expected-failures-added.txt
new file mode 100644
index 0000000..a327425
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-failures-added.txt
@@ -0,0 +1,4 @@
+fast/bar/betz/fail3.html
+fast/bar/fail2.html
+fast/foo/fail1.html
+fast/pass1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-failures-new-crash.txt b/webkit/tools/layout_tests/testdata/expected-failures-new-crash.txt
new file mode 100644
index 0000000..7c25263
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-failures-new-crash.txt
@@ -0,0 +1,4 @@
+fast/bar/betz/crash3.html
+fast/bar/betz/fail3.html
+fast/bar/fail2.html
+fast/foo/fail1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-failures-new-passing.txt b/webkit/tools/layout_tests/testdata/expected-failures-new-passing.txt
new file mode 100644
index 0000000..d2d2e79
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-failures-new-passing.txt
@@ -0,0 +1,2 @@
+fast/bar/betz/fail3.html
+fast/foo/fail1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-failures-new-test.txt b/webkit/tools/layout_tests/testdata/expected-failures-new-test.txt
new file mode 100644
index 0000000..9492214
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-failures-new-test.txt
@@ -0,0 +1,4 @@
+fast/bar/betz/fail3.html
+fast/bar/fail2.html
+fast/foo/fail1.html
+new-test.html
diff --git a/webkit/tools/layout_tests/testdata/expected-failures.txt b/webkit/tools/layout_tests/testdata/expected-failures.txt
new file mode 100644
index 0000000..0e637e1
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-failures.txt
@@ -0,0 +1,3 @@
+fast/bar/betz/fail3.html
+fast/bar/fail2.html
+fast/foo/fail1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-passing-new-baseline.txt b/webkit/tools/layout_tests/testdata/expected-passing-new-baseline.txt
new file mode 100644
index 0000000..de8a4b6
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-passing-new-baseline.txt
@@ -0,0 +1 @@
+fast/foo/pass2.html
diff --git a/webkit/tools/layout_tests/testdata/expected-passing-new-passing.txt b/webkit/tools/layout_tests/testdata/expected-passing-new-passing.txt
new file mode 100644
index 0000000..3bac884
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-passing-new-passing.txt
@@ -0,0 +1,3 @@
+fast/bar/betz/crash3.html
+fast/foo/pass2.html
+fast/pass1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-passing-new-passing2.txt b/webkit/tools/layout_tests/testdata/expected-passing-new-passing2.txt
new file mode 100644
index 0000000..72acc8a
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-passing-new-passing2.txt
@@ -0,0 +1,3 @@
+fast/bar/fail2.html
+fast/foo/pass2.html
+fast/pass1.html
diff --git a/webkit/tools/layout_tests/testdata/expected-passing-new-test.txt b/webkit/tools/layout_tests/testdata/expected-passing-new-test.txt
new file mode 100644
index 0000000..5b5b0ad
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-passing-new-test.txt
@@ -0,0 +1,3 @@
+fast/foo/pass2.html
+fast/pass1.html
+new-test.html
diff --git a/webkit/tools/layout_tests/testdata/expected-passing.txt b/webkit/tools/layout_tests/testdata/expected-passing.txt
new file mode 100644
index 0000000..2cdebb4
--- /dev/null
+++ b/webkit/tools/layout_tests/testdata/expected-passing.txt
@@ -0,0 +1,2 @@
+fast/foo/pass2.html
+fast/pass1.html
diff --git a/webkit/tools/leak_tests/run_node_leak_test.py b/webkit/tools/leak_tests/run_node_leak_test.py
new file mode 100644
index 0000000..3d5a815
--- /dev/null
+++ b/webkit/tools/leak_tests/run_node_leak_test.py
@@ -0,0 +1,303 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Run node leak tests using the test_shell.
+
+TODO(pjohnson): Add a way for layout tests (and other local files in the
+working copy) to be easily run by specifying paths relative to webkit (or
+something similar).
+"""
+
+import logging
+import optparse
+import os
+import random
+import re
+import sys
+
+import google.logging_utils
+import google.path_utils
+import google.platform_utils
+import google.process_utils
+
+# Magic exit code to indicate a new fix.
+REBASELINE_EXIT_CODE = -88
+
+# Status codes.
+PASS, FAIL, REBASELINE = range(3)
+
+# The test list files are found in this subdirectory, which must be a sibling
+# to this script itself.
+TEST_FILE_DIR = 'test_lists'
+
+# TODO(pjohnson): Find a way to avoid this duplicate code. This function has
+# been shamelessly taken from layout_tests/layout_package.
+_webkit_root = None
+
+def WebKitRoot():
+ """Returns the full path to the directory containing webkit.sln. Raises
+ PathNotFound if we're unable to find webkit.sln.
+ """
+
+ global _webkit_root
+ if _webkit_root:
+ return _webkit_root
+ webkit_sln_path = google.path_utils.FindUpward(google.path_utils.ScriptDir(),
+ 'webkit.sln')
+ _webkit_root = os.path.dirname(webkit_sln_path)
+ return _webkit_root
+
+def GetAbsolutePath(path):
+ platform_util = google.platform_utils.PlatformUtility(WebKitRoot())
+ return platform_util.GetAbsolutePath(path)
+
+# TODO(pjohnson): Find a way to avoid this duplicated code. This function has
+# been mostly copied from another function, TestShellBinary, in
+# layout_tests/layout_package.
+def TestShellTestBinary(target):
+ """Gets the full path to the test_shell_tests binary for the target build
+ configuration. Raises PathNotFound if the file doesn't exist.
+ """
+
+ full_path = os.path.join(WebKitRoot(), target, 'test_shell_tests.exe')
+ if not os.path.exists(full_path):
+ # Try chrome's output directory in case test_shell was built by chrome.sln.
+ full_path = google.path_utils.FindUpward(WebKitRoot(), 'chrome', target,
+ 'test_shell_tests.exe')
+ if not os.path.exists(full_path):
+ raise PathNotFound('unable to find test_shell_tests at %s' % full_path)
+ return full_path
+
+class NodeLeakTestRunner:
+ """A class for managing running a series of node leak tests.
+ """
+
+ def __init__(self, options, urls):
+ """Collect a list of URLs to test.
+
+ Args:
+ options: a dictionary of command line options
+ urls: a list of URLs in the format:
+ (url, expected_node_leaks, expected_js_leaks) tuples
+ """
+
+ self._options = options
+ self._urls = urls
+
+ self._test_shell_test_binary = TestShellTestBinary(options.target)
+
+ self._node_leak_matcher = re.compile('LEAK: (\d+) Node')
+ self._js_leak_matcher = re.compile('Leak (\d+) JS wrappers')
+
+ def RunCommand(self, command):
+ def FindMatch(line, matcher, group_number):
+ match = matcher.match(line)
+ if match:
+ return int(match.group(group_number))
+ return 0
+
+ (code, output) = google.process_utils.RunCommandFull(command, verbose=True,
+ collect_output=True,
+ print_output=False)
+ node_leaks = 0
+ js_leaks = 0
+
+ # Print a row of dashes.
+ if code != 0:
+ print '-' * 75
+ print 'OUTPUT'
+ print
+
+ for line in output:
+ # Sometimes multiple leak lines are printed out, which is why we
+ # accumulate them here.
+ node_leaks += FindMatch(line, self._node_leak_matcher, 1)
+ js_leaks += FindMatch(line, self._js_leak_matcher, 1)
+
+ # If the code indicates there was an error, print the output to help
+ # figure out what happened.
+ if code != 0:
+ print line
+
+ # Print a row of dashes.
+ if code != 0:
+ print '-' * 75
+ print
+
+ return (code, node_leaks, js_leaks)
+
+ def RunUrl(self, test_url, expected_node_leaks, expected_js_leaks):
+ shell_args = ['--gtest_filter=NodeLeakTest.*TestURL',
+ '--time-out-ms=' + str(self._options.time_out_ms),
+ '--test-url=' + test_url,
+ '--playback-mode']
+
+ if self._options.cache_dir != '':
+ shell_args.append('--cache-dir=' + self._options.cache_dir)
+
+ command = [self._test_shell_test_binary] + shell_args
+ (exit_code, actual_node_leaks, actual_js_leaks) = self.RunCommand(command)
+
+ logging.info('%s\n' % test_url)
+
+ if exit_code != 0:
+ # There was a crash, or something else went wrong, so duck out early.
+ logging.error('Test returned: %d\n' % exit_code)
+ return FAIL
+
+ result = ('TEST RESULT\n'
+ ' Node Leaks: %d (actual), %d (expected)\n'
+ ' JS Leaks: %d (actual), %d (expected)\n' %
+ (actual_node_leaks, expected_node_leaks,
+ actual_js_leaks, expected_js_leaks))
+
+ success = (actual_node_leaks <= expected_node_leaks and
+ actual_js_leaks <= expected_js_leaks)
+
+ if success:
+ logging.info(result)
+ else:
+ logging.error(result)
+ logging.error('Unexpected leaks found!\n')
+ return FAIL
+
+ if (expected_node_leaks > actual_node_leaks or
+ expected_js_leaks > actual_js_leaks):
+ logging.warn('Expectations may need to be re-baselined.\n')
+ # TODO(pjohnson): Return REBASELINE here once bug 1177263 is fixed and
+ # the expectations have been lowered again.
+
+ return PASS
+
+ def Run(self):
+ status = PASS
+ results = [0, 0, 0]
+ failed_urls = []
+ rebaseline_urls = []
+
+ for (test_url, expected_node_leaks, expected_js_leaks) in self._urls:
+ result = self.RunUrl(test_url, expected_node_leaks, expected_js_leaks)
+ if result == PASS:
+ results[PASS] += 1
+ elif result == FAIL:
+ results[FAIL] += 1
+ failed_urls.append(test_url)
+ status = FAIL
+ elif result == REBASELINE:
+ results[REBASELINE] += 1
+ rebaseline_urls.append(test_url)
+ if status != FAIL:
+ status = REBASELINE
+ return (status, results, failed_urls, rebaseline_urls)
+
+def main(options, args):
+ if options.seed != None:
+ random.seed(options.seed)
+
+ # Set up logging so any messages below logging.WARNING are sent to stdout,
+ # otherwise they are sent to stderr.
+ google.logging_utils.config_root(level=logging.INFO,
+ threshold=logging.WARNING)
+
+ if options.url_list == '':
+ logging.error('URL test list required')
+ sys.exit(1)
+
+ url_list = os.path.join(os.path.dirname(sys.argv[0]), TEST_FILE_DIR,
+ options.url_list)
+ url_list = GetAbsolutePath(url_list);
+
+ lines = []
+ file = None
+ try:
+ file = open(url_list, 'r')
+ lines = file.readlines()
+ finally:
+ if file != None:
+ file.close()
+
+ expected_matcher = re.compile('(\d+)\s*,\s*(\d+)')
+
+ urls = []
+ for line in lines:
+ line = line.strip()
+ if len(line) == 0 or line.startswith('#'):
+ continue
+ list = line.rsplit('=', 1)
+ if len(list) < 2:
+ logging.error('Line "%s" is not formatted correctly' % line)
+ continue
+ match = expected_matcher.match(list[1].strip())
+ if not match:
+ logging.error('Line "%s" is not formatted correctly' % line)
+ continue
+ urls.append((list[0].strip(), int(match.group(1)), int(match.group(2))))
+
+ random.shuffle(urls)
+ runner = NodeLeakTestRunner(options, urls)
+ (status, results, failed_urls, rebaseline_urls) = runner.Run()
+
+ logging.info('SUMMARY\n'
+ ' %d passed\n'
+ ' %d failed\n'
+ ' %d re-baseline\n' %
+ (results[0], results[1], results[2]))
+
+ if len(failed_urls) > 0:
+ failed_string = '\n'.join(' ' + url for url in failed_urls)
+ logging.error('FAILED URLs\n%s\n' % failed_string)
+
+ if len(rebaseline_urls) > 0:
+ rebaseline_string = '\n'.join(' ' + url for url in rebaseline_urls)
+ logging.warn('RE-BASELINE URLs\n%s\n' % rebaseline_string)
+
+ if status == FAIL:
+ return 1
+ elif status == REBASELINE:
+ return REBASELINE_EXIT_CODE
+ return 0
+
+if '__main__' == __name__:
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('', '--target', default='Debug',
+ help='build target (Debug or Release)')
+ option_parser.add_option('', '--cache-dir', default='',
+ help='use a specified cache directory')
+ option_parser.add_option('', '--url-list', default='',
+ help='URL input file, with leak expectations, '
+ 'relative to webkit/tools/leak_tests')
+ option_parser.add_option('', '--time-out-ms', default=30000,
+ help='time out for each test')
+ option_parser.add_option('', '--seed', default=None,
+ help='seed for random number generator, use to '
+ 'reproduce the exact same order for a '
+ 'specific run')
+ options, args = option_parser.parse_args()
+ sys.exit(main(options, args))
diff --git a/webkit/tools/leak_tests/test_lists/alexa_100.txt b/webkit/tools/leak_tests/test_lists/alexa_100.txt
new file mode 100644
index 0000000..8c04a02
--- /dev/null
+++ b/webkit/tools/leak_tests/test_lists/alexa_100.txt
@@ -0,0 +1,92 @@
+# Format: URL = EXPECTED_NODE_LEAKS, EXPECTED_JS_LEAKS
+#
+# This is a list of the top 100 Alexa URLs (as of 3/28/2008) that loaded
+# successfully into our saved cache.
+#
+# Flaky URLs are covered by bug 1177263.
+# Since this test is so flaky, for now set the expected numbers very high to
+# catch only the most egregious regressions.
+
+http://www.yahoo.com = 200,200
+http://www.youtube.com = 200,200
+http://www.live.com = 200,200
+http://www.google.com = 200,200
+http://www.myspace.com = 200,200
+http://www.facebook.com = 200,200
+http://www.msn.com = 200,200
+http://www.hi5.com = 200,200
+http://www.wikipedia.org = 200,200
+http://www.orkut.com = 200,200
+http://www.blogger.com = 200,200
+http://www.fotolog.net = 200,200
+http://www.google.fr = 200,200
+http://www.friendster.com = 200,200
+http://www.microsoft.com = 200,200
+http://www.baidu.com = 200,200
+http://www.megarotic.com = 200,200
+http://www.google.cl = 200,200
+http://www.yahoo.co.jp = 200,200
+http://www.ebay.com = 200,200
+http://www.google.com.br = 200,200
+http://www.google.es = 200,200
+http://www.seznam.cz = 200,200
+http://www.google.com.mx = 200,200
+http://www.dailymotion.com = 200,200
+http://www.photobucket.com = 200,200
+http://www.youporn.com = 200,200
+http://www.imdb.com = 200,200
+http://www.google.pl = 200,200
+http://www.qq.com = 200,200
+http://www.google.co.uk = 200,200
+http://www.flickr.com = 200,200
+http://www.vkontakte.ru = 200,200
+http://www.nasza-klasa.pl = 200,200
+http://www.odnoklassniki.ru = 200,200
+http://www.google.de = 200,200
+http://www.metroflog.com = 200,200
+http://www.google.co.ve = 200,200
+http://www.google.com.ar = 200,200
+http://www.free.fr = 200,200
+http://www.wordpress.com = 200,200
+http://www.wretch.cc = 200,200
+http://www.mininova.org = 200,200
+http://www.onet.pl = 200,200
+http://www.google.com.pe = 200,200
+http://www.aol.com = 200,200
+http://www.google.com.co = 200,200
+http://www.allegro.pl = 200,200
+http://www.yandex.ru = 200,200
+http://www.deviantart.com = 200,200
+http://www.sina.com.cn = 200,200
+http://www.google.co.in = 200,200
+http://www.bbc.co.uk = 200,200
+http://www.google.ca = 200,200
+http://www.craigslist.org = 200,200
+http://www.google.sk = 200,200
+http://www.livejournal.com = 200,200
+http://www.iwiw.hu = 200,200
+http://www.google.com.vn = 200,200
+http://www.globo.com = 200,200
+http://www.wp.pl = 200,200
+http://www.netlog.com = 200,200
+http://www.perfspot.com = 200,200
+http://www.googlesyndication.com = 200,200
+http://www.google.it = 200,200
+http://www.google.co.hu = 200,200
+http://www.fc2.com = 200,200
+http://www.google.cn = 200,200
+http://www.ebay.fr = 200,200
+http://www.veoh.com = 200,200
+http://www.google.co.th = 200,200
+http://www.fotka.pl = 200,200
+http://www.orange.fr = 200,200
+http://www.google.com.tr = 200,200
+http://www.geocities.com = 200,200
+http://www.apple.com = 200,200
+http://www.onemanga.com = 200,200
+http://www.ebay.de = 200,200
+http://www.google.co.jp = 200,200
+http://www.taobao.com = 200,200
+http://www.megaflirt.com = 200,200
+http://www.ebay.co.uk = 200,200
+http://www.sexyono.com = 200,200
diff --git a/webkit/tools/merge/gen-merge-diff.sh b/webkit/tools/merge/gen-merge-diff.sh
new file mode 100755
index 0000000..10aa0fb
--- /dev/null
+++ b/webkit/tools/merge/gen-merge-diff.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+# Copyright 2007 Google Inc.
+# All rights reserved.
+#
+# This script expects to live in chrome/tools/branch/. If it's ever moved,
+# the value of |root| will need to be changed below.
+#
+# Generate a helper script that does a 3-way diff on each forked WebKit file
+# you give it. The diff will be among:
+# base: original third_party file from a Chrome trunk checkout
+# diff1: updated third_party file from the merge branch
+# diff2: forked file in webkit/pending or wherever.
+#
+# Run this script from the merge branch. Give it the path to the root of your
+# trunk checkout (the directory containing src and data directories) as the
+# first argument, and a list of forked files (e.g. from either copy of
+# webkit/pending/) as the second argument.
+#
+# Example usage:
+# ./gen-merge-diff.sh /cygdrive/c/src/chrome/trunk webkit/pending/*.*
+#
+# Once the helper script is generated, you can run it to do the 3-way diff.
+
+wkfilelist=/tmp/webkit-branch-merge/third_party_list.txt
+
+execdir=`dirname $0`
+root=$execdir/../..
+
+origtrunk=$1
+origroot=$origtrunk
+shift
+
+if [[ $# -lt 1 || ! -d "$origroot/third_party" ]]; then
+ echo "Usage: $0 <path-to-the-base-dir-of-chrome-trunk> [list-of-forked-files]"
+ echo "Example: $0 /cygdrive/c/src/chrome/trunk webkit/pending/*.*"
+ exit 1
+fi
+
+if which p4merge.exe >& /dev/null; then
+ merge_exe=p4merge.exe
+else
+ merge_exe="/cygdrive/c/Program Files/Perforce/p4merge.exe"
+ if [ ! -e "$merge_exe" ]; then
+ echo "WARNING: It looks like you don't have p4merge installed."
+ echo "You must edit merge-diff.sh and change the diff3way function to"
+ echo "point at a valid merge tool."
+ fi
+fi
+
+if [ -e $wkfilelist ]; then
+ echo "Using cached file listing of third_party."
+ echo "WARNING: If the cached file is not up to date, you should execute:"
+ echo " rm $wkfilelist"
+ echo "and run this script again."
+else
+ echo "Creating file listing of third_party..."
+ (cd $root && find third_party -type f | grep -v '/\.svn' | grep -v 'ForwardingHeaders' | grep -v 'DerivedSources' > $wkfilelist)
+fi
+
+# Prepare the helper script.
+
+cat > merge-diff.sh <<EOF
+#!/bin/sh
+
+function diff3way {
+ a=\`cygpath -wa \$1\`
+ b=\`cygpath -wa \$2\`
+ c=\`cygpath -wa \$3\`
+ "$merge_exe" "\$a" "\$b" "\$c" "\$c"
+}
+
+EOF
+
+chmod +x merge-diff.sh
+
+# Now add all the diff commands to the helper script.
+
+for each in "$@"; do
+ filename=`echo $each | sed -e "s,.*/,,g"`
+ tpfile=`grep "/\<$filename\>" $wkfilelist`
+ if [ "$tpfile" != "" ]; then
+ # Only run the 3-way diff if the upstream file has changed
+ ignored=`diff "$origroot/$tpfile" "$root/$tpfile"`
+ if [ $? != 0 ]; then
+ echo "diff3way \"$origroot/$tpfile\" \"$root/$tpfile\" \"$each\"" >> merge-diff.sh
+ fi
+ fi
+done
+
+cat << EOF
+
+======================
+Created: merge-diff.sh
+
+Please edit this file to make sure it is correct. Then run it with
+'./merge-diff.sh'.
+EOF
diff --git a/webkit/tools/merge/update-branch-webkit.py b/webkit/tools/merge/update-branch-webkit.py
new file mode 100644
index 0000000..8f61003
--- /dev/null
+++ b/webkit/tools/merge/update-branch-webkit.py
@@ -0,0 +1,774 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+Update a copy of WebKit to a given version, issuing the necessary gvn commands.
+
+
+This script is intended for internal use only, by knowledgeable users, and
+therefore does not taint-check user-supplied values.
+
+Cygwin is required to run this script, for 'cygpath'.
+
+Only tested on Windows, running in Cygwin. Most of this script should
+be compatible with Linux as well. The paths may not be correct for
+running in a Windows cmd shell.
+
+TODO(pamg): Test in Windows cmd shell and fix problems.
+
+Usage notes:
+ In normal usage, it's expected that the user will first run svn manually
+ to update a local copy of the WebKit trunk to a known (or head) revision.
+ The svn checkout is prone to odd failures (network timeouts, etc.), so
+ it's safer to run it by hand so you can more easily notice and recover
+ from any problems. Note that if you're running svn more than once, to
+ update more than one directory, you'll want to specify a --revision (-r)
+ on any calls after the first, just in case someone's landed something
+ while you were checking out the first directory.
+ For example:
+
+ cd /tmp/webkit-branch-merge/WebKit
+ svn co http://svn.webkit.org/repository/webkit/trunk/WebCore WebCore
+ [...Checked out revision 19776.]
+ svn co -r 19776 \
+ http://svn.webkit.org/repository/webkit/trunk/JavaScriptCore \
+ JavaScriptCore
+ cd /path/to/webkit/tools/merge
+ python ./update-branch-webkit.py --diff --real \
+ --old /path/to/mergebranch/ 19776
+
+ Here the --old parameter points to the root of your merge branch checkout,
+ containing the src/ and data/ directories. You can also append
+ --new [directory name] to point to the directory where you've checked out
+ the latest webkit files (if you checked them out to somewhere other than
+ /tmp/webkit-branch-merge/WebKit as depicted above). The --new parameter
+ should be a "WebKit" directory that corresponds to the webkit svn trunk.
+
+ If you want to see what the script will do before doing it, remove the
+ --real option. When you're satisfied that it's doing the right thing, you
+ can restore --real and run it one more time. In that case, you can also
+ leave off --diff on each run after the first, allowing the script to use a
+ cached file of diffs rather than re-generating it each time. Other options
+ are available as well; see 'update-branch-webkit --help' for a full list.
+
+ Directories may be specified with Windows-style paths, but the slashes
+ must be forward-slashes, not backslashes, or the Cygwin shell will remove
+ them.
+"""
+
+import errno
+import logging
+import optparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import traceback
+
+# Whether to produce additional debugging information.
+DEBUGGING = False
+
+# The filename holding the new WebKit Subversion revision number.
+BASE_REV_FILE = 'BASE_REVISION'
+
+# The URL from which to checkout the new revision if needed.
+SVN_URL = 'http://svn.webkit.org/repository/webkit/trunk'
+
+# The files in which to save lists of added, deleted, and edited files.
+SAVE_ADDED_FILES = 'merge-add-files.txt'
+SAVE_DELETED_FILES = 'merge-delete-files.txt'
+SAVE_EDITED_FILES = 'merge-edit-files.txt'
+SAVE_OBSOLETE_FILES = 'merge-obsolete-files.txt'
+
+# Executable names, found in the system PATH.
+SVN = 'svn.exe'
+DIFF = 'diff.exe'
+CYGPATH = 'cygpath.exe'
+
+# Directories to be entirely ignored when updating the merge branch.
+IGNORE_DIRS = ['.svn', 'DerivedSources']
+
+# Global cygpath process.
+_cygpath_proc = None
+
+########################################
+# Error classes
+
+class LocalError(Exception):
+ """Base class for local errors."""
+ stack_trace = True
+ code = 1
+
+class ArgumentError(LocalError):
+ """Exception raised for errors in parsed script arguments."""
+ stack_trace = False
+ code = 2
+
+class CannotFindError(LocalError):
+ """Exception raised for an expected file or directory that does not exist."""
+ def __init__(self, description, path):
+ LocalError.__init__(self, "Can't find %s '%s'" % (description, path))
+ stack_trace = False
+ code = 3
+
+class CannotWriteError(LocalError):
+ """Exception raised when writing to a necessary file fails."""
+ def __init__(self, path):
+ LocalError.__init__(self, "Can't write to '%s'" % path)
+ stack_trace = False
+ code = 4
+
+class CommandError(LocalError):
+ """Exception raised when an external command fails."""
+ def __init__(self, prefix, value):
+ """
+ Build a descriptive error message using available information.
+
+ Args:
+ prefix: Descriptive text prepended to the error message.
+ value: Error value as provided in the exception, if any. May be None.
+ """
+ if value is not None:
+ (errnum, message) = value
+ LocalError.__init__(self, "%s: %s (%s)" % (prefix, message, errnum))
+ else:
+ LocalError.__init__(self, prefix)
+ stack_trace = False
+ code = 5
+
+
+########################################
+# Utility functions
+
+def GetAbsolutePath(path):
+ """Convert an unknown-style path to an absolute, mixed-style Windows path.
+
+ We use an external cygpath binary to do the conversion. For performance
+ reasons, we use a single cygpath process.
+ """
+ global _cygpath_proc
+ if not _cygpath_proc:
+ _cygpath_proc = subprocess.Popen([CYGPATH, '-a', '-m', '-f', '-'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ _cygpath_proc.stdin.write(path + '\n')
+ return _cygpath_proc.stdout.readline().rstrip()
+
+def PathFromChromeRoot(subdir):
+ """Return the path of the given WebKit path relative to the Chrome merge dir.
+
+ We could get fancy and search for these, but it's so much simpler to just
+ set them, it's arguably even easier to maintain that way.
+
+ Args:
+ subdir: The relative path to be converted. It may be a directory or a
+ filename, and it should be specified relative to the WebKit checkout
+ (e.g., 'WebCore').
+
+ Returns:
+ The path to the given subdirectory dir in the Chrome webkit tree, starting
+ from the src directory.
+ """
+ return os.path.join('third_party', subdir)
+
+def NewDir(subdir=''):
+ """Return an absolute path to the subdirectory within the WebKit checkout.
+
+ The subdir is specified relative to the WebKit checkout (e.g., 'WebCore').
+ """
+ return GetAbsolutePath(os.path.join(options.new, subdir))
+
+def OldDir(subdir=''):
+ """Return an absolute path to the given subdirectory within the merge branch.
+
+ The subdir is specified relative to the WebKit checkout (e.g., 'WebCore').
+ """
+ reldir = PathFromChromeRoot(subdir)
+ return GetAbsolutePath(os.path.join(options.old, reldir))
+
+def TempDir(relative_path=''):
+ """Return an absolute path to the given location in the temp directory."""
+ return GetAbsolutePath(os.path.join(options.temp_dir, relative_path))
+
+def CollectFileList(search_dir, old):
+ """Recursively collect a set of files within the given directory.
+
+ FIXME(pamg): This function assumes that there are no instances of the string
+ given by the search_dir argument in either of the source paths (old and
+ new). That is, if dir is 'WebCore', the --old and --new options must not
+ contain the string 'WebCore'.
+
+ Args:
+ search_dir: The directory within which to search. It should be specified
+ relative to the WebKit checkout (e.g., 'WebCore').
+ old: Boolean value indicating whether to look for files in the old
+ directory (i.e., the Chrome merge branch) or the new one (the WebKit
+ checkout).
+
+ Returns:
+ A set of file paths relative to (and including) the given dir, with no
+ leading slash.
+ """
+ # FIXME(pamg): This replacement is why --old and --new can't contain
+ # search_dir.
+ if old:
+ path = OldDir(search_dir)
+ basedir = path.replace(search_dir, '')
+ else:
+ path = NewDir(search_dir)
+ basedir = path.replace(search_dir, '')
+
+ result = set()
+ for root, dirs, files in os.walk(path):
+ # We want the directory from search_dir onward, and we need '/' separators
+ # to match what diff returns.
+ relative_dir = root.replace(basedir, '', 1).replace('\\', '/')
+ for filename in files:
+ if not IgnoreFile(root):
+ result.add('/'.join([relative_dir, filename]))
+ return result
+
+def MakeDirectory(path):
+ """Create a path and its parents, returning a set of the paths created."""
+
+ logging.debug('Making directory %s' % path)
+ add = set()
+ (basepath, subdir) = os.path.split(path)
+ if not os.path.exists(basepath):
+ add = add.union(MakeDirectory(basepath))
+ if options.real:
+ os.mkdir(path)
+ add.add(path)
+ return add
+
+def IsAvailable(executable):
+ """Return True if the given executable can be found.
+
+ An executable is deemed available if it exists (for an absolute or relative
+ path) or is in the system path (for a simple filename). This is a naive
+ implementation that assumes that if a file with the given name exists, it is
+ accessible and executable.
+ """
+
+ if os.path.dirname(executable) != '':
+ # This is a pathname, either relative or absolute.
+ return os.path.exists(executable)
+
+ paths = os.environ['PATH'].split(os.pathsep)
+ for path_dir in paths:
+ if os.path.exists(os.path.join(path_dir, executable)):
+ return True
+ return False
+
+def IgnoreFile(path):
+ """Return True if the path is in the list of directories to be ignored."""
+ for ignore_dir in IGNORE_DIRS:
+ path = path.replace('\\', '/')
+ path = '/' + path + '/'
+ if '/' + ignore_dir + '/' in path:
+ return True
+ return False
+
+def WriteFile(path, data):
+ """Write the given data to the path.
+
+ Args:
+ path: The absolute filename to be written.
+ data: The string data to be written to the file.
+
+ Raises:
+ CannotWriteError if the write fails.
+ """
+ logging.debug('Writing file to %s' % path)
+ try:
+ f = open(path, 'w')
+ f.write(data)
+ f.close()
+ except IOError, e:
+ raise CannotWriteError(path)
+
+def WriteAbsoluteFileList(filename, file_list):
+ """Write a file containing a sorted list of absolute paths in the branch.
+
+ Args:
+ filename: The file to write.
+ file_list: An unsorted list of file paths, given relative to the WebKit
+ checkout (e.g., 'WebCore/ChangeLog').
+ """
+ logging.info('Saving %s' % filename)
+ file_paths = [OldDir(x) for x in file_list]
+ # Sort after converting to absolute paths, in case some of the incoming paths
+ # were already absolute.
+ file_paths.sort()
+ WriteFile(filename, '\n'.join(file_paths))
+
+def RunShellCommand(command, description, run=True):
+ """Run a command in the shell, waiting for completion.
+
+ Args:
+ command: The shell command to be run, as a string.
+ description: A brief description of the command, to be used in an error
+ message if needed.
+ run: If false, the command will be logged, but it won't actually be
+ executed.
+
+ Raises:
+ CommandError if the command runs but exits with a nonzero status.
+ """
+ logging.debug(command)
+ if run:
+ result = subprocess.call(command, shell=True)
+ if result:
+ raise CommandError(description, None)
+
+
+########################################
+# Task functions.
+
+def ValidateArguments():
+ """Validate incoming options.
+
+ Canonicalize paths and make sure that they exist, make sure a diff file
+ exists if one is being used, and check for all required executables.
+ """
+ if options.revision is None:
+ raise ArgumentError('Please specify a --revision.')
+ try:
+ # We need the revision as a string, but we want to make sure it's numeric.
+ options.revision = str(int(options.revision))
+ except ValueError:
+ raise ArgumentError('Revision must be a number.')
+ if options.revision < 1:
+ raise ArgumentError('Revision must be at least 1.')
+
+ options.dir_list = options.directories.split(',')
+ if not len(options.dir_list):
+ raise ArgumentError('Please list at least one directory.')
+
+ options.old = GetAbsolutePath(options.old)
+ options.new = GetAbsolutePath(options.new)
+ options.temp_dir = GetAbsolutePath(options.temp_dir)
+
+ if not os.path.exists(options.old):
+ raise CannotFindError('old dir', options.old)
+ if not os.path.exists(options.new):
+ raise CannotFindError('new dir', options.new)
+ for dir_name in options.dir_list:
+ if not os.path.exists(os.path.join(options.old, 'third_party', dir_name)):
+ raise CannotFindError('old %s' % dir_name, '')
+ if not os.path.exists(os.path.join(options.new, dir_name)):
+ raise CannotFindError('new %s' % dir_name, '')
+ if not os.path.exists(options.temp_dir):
+ logging.info('Creating temp directory %s' % options.temp_dir)
+ os.makedirs(options.temp_dir)
+
+ saved_diff_path = TempDir(SAVE_EDITED_FILES)
+ if not options.diff and not os.path.exists(saved_diff_path):
+ logging.warning("No saved diff file at '%s'. Running diff again." %
+ saved_diff_path)
+ options.diff = True
+
+ if options.svn_checkout:
+ if not IsAvailable(SVN):
+ raise CannotFindError('executable', SVN)
+ else:
+ checkout_dir = NewDir()
+ if not IsAvailable(checkout_dir):
+ raise CannotFindError('new WebKit checkout', checkout_dir)
+ if options.diff:
+ if not IsAvailable(DIFF):
+ raise CannotFindError('executable', DIFF)
+ if not IsAvailable('gvn.bat'):
+ raise CannotFindError('batch file', 'gvn.bat')
+
+def RunSvnCheckout(revision):
+ """Use svn to checkout a new WebKit from SVN_URL into the temp dir."""
+ logging.info('Checking out revision %s', revision)
+ for checkout_dir in options.dir_list:
+ new_dir = NewDir(checkout_dir)
+ url = SVN_URL + '/' + checkout_dir
+ command = '%s checkout -r %s %s %s' % (SVN, revision, url, new_dir)
+ RunShellCommand(command, 'Running svn')
+
+def UpdateBaseRevision(revision):
+ """Update the merge branch BASE_REVISION file."""
+ new_baserev_file = NewDir(BASE_REV_FILE)
+ try:
+ existing_revision = open(new_baserev_file, 'r').read().rstrip()
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ existing_revision = -1
+ else:
+ raise
+ if revision != existing_revision:
+ logging.info('Updating %s to %s' % (new_baserev_file, revision))
+ WriteFile(new_baserev_file, revision)
+
+def RunDiff(saved_file):
+ """Collect a set of files that differ between the WebKit and Chrome trees.
+
+ Run a recursive diff for each of the relevant directories, collecting a set
+ of files that differ between them. Save the resulting list in a file, and
+ also return the set.
+
+ Args:
+ saved_file: The file in which to save the list of differing files.
+
+ Returns:
+ A set of paths to files that differ between the two trees. Paths are
+ given relative to the WebKit checkout (e.g., 'WebCore/ChangeLog').
+
+ Raises:
+ CommandError if the diff command returns an error or produces unrecognized
+ output.
+ """
+ # Regular expressions for parsing the output of the diff.
+ add_delete_re = re.compile("^Only in [^ ]+")
+ edit_re = re.compile("^Files ([^ ]+) and [^ ]+ differ")
+
+ # Generate a set of the possible leading directory strings, to be removed
+ # from the file paths reported by diff.
+ dir_replacements = set()
+ for checkout_dir in options.dir_list:
+ # FIXME(pamg): This assumes that the name of the dir being processed is
+ # not a substring of options.new. See the FIXME for CollectFileList.
+ base_dir = NewDir(checkout_dir).replace(checkout_dir, '')
+ dir_replacements.add(base_dir)
+
+ # Collect the set of edited files.
+ edited = set()
+ for diff_dir in options.dir_list:
+ logging.info('Generating diffs for %s' % diff_dir)
+ command = [DIFF, '--recursive', '--ignore-space-change', '--brief',
+ NewDir(diff_dir), OldDir(diff_dir)]
+ logging.debug(command)
+ try:
+ p = subprocess.Popen(command, stdout=subprocess.PIPE)
+ diff_text = p.stdout.read().replace('\r', '')
+ rc = p.wait()
+ except OSError, e:
+ raise CommandError('Running diff ' + command, e)
+ # Diff exit status is 0 if the directories compared are the same, 1 if
+ # they differ, or 2 if an error was encountered.
+ if rc < 0 or rc > 1:
+ raise CommandError('Error running diff command', None)
+
+ # Examine this directory's diff result and build a list of changed files.
+ # Although we're not interested in added or deleted files, we parse the
+ # "Only in..." lines to catch any errors.
+ # Files C:/new/directory/file.h and C:/old/directory/file.h differ
+ # (ignored) Only in C:/new/directory: new_filename.h
+ # (ignored) Only in C:/old/directory: old_filename.h
+ logging.info('Analyzing %s diff results' % diff_dir)
+ for diff_line in diff_text.split('\n'):
+ if IgnoreFile(diff_line):
+ continue
+ if edit_re.search(diff_line) is not None:
+ filename = re.sub(edit_re, r'\1', diff_line)
+ for replace in dir_replacements:
+ filename = filename.replace(replace, '', 1)
+ edited.add(filename)
+ elif add_delete_re.search(diff_line) is not None:
+ # Do nothing, but don't complain about an error.
+ pass
+ elif diff_line != '':
+ raise CommandError("Unknown diff result '%s'" % diff_line, None)
+
+ WriteFile(saved_file, '\n'.join(sorted(edited)))
+ return edited
+
+def CopyFiles(files):
+ """Copy files from WebKit to the merge branch, collecting added directories.
+
+ If options.real is False, collect the directories that would be added, but
+ don't actually make the directories or perform the copies.
+
+ Args:
+ files: The set of files to be copied, with paths given relative to the
+ WebKit checkout (e.g., 'WebCore/ChangeLog').
+
+ Returns:
+ A set of absolute directory paths that were created in the merge branch
+ in order to copy the files.
+
+ Raises:
+ CommandError if the file copy fails.
+ """
+ logging.info('Copying files from %s to %s' % (options.new, options.old))
+ added = set()
+ for file in files:
+ # Create the directory if it doesn't exist.
+ # TODO(pamg): This has a lot of redundancy and could probably be made
+ # faster.
+ dirname = os.path.dirname(file)
+ old_dir = OldDir(dirname)
+ if not os.path.exists(old_dir):
+ # We need to handle each new directory level individually, for gvn add.
+ added = added.union(MakeDirectory(old_dir))
+ new_path = NewDir(file)
+ old_path = OldDir(file)
+ if options.real:
+ try:
+ shutil.copyfile(new_path, old_path)
+ except IOError, e:
+ raise CommandError('Copying file', e)
+ return added
+
+def CopyObsoleteFiles(files):
+ """Copy new versions of obsolete header files into .obsolete names.
+
+ If options.real is False, log the copy but do not actually perform it.
+
+ Args:
+ files: The set of header files to be copied, with paths given relative to
+ the WebKit checkout and not including '.obsolete' (e.g.,
+ 'WebCore/html/HTMLElement.h'). Their directories should already exist.
+
+ Raises:
+ CommandError if the file copy fails.
+ """
+ for file in files:
+ # The directory should already exist.
+ new_path = NewDir(file)
+ old_path = OldDir(file + '.obsolete')
+ logging.debug('Copying %s to %s' % (new_path, old_path))
+ if options.real:
+ try:
+ shutil.copyfile(new_path, old_path)
+ except IOError, e:
+ raise CommandError('Copying obsolete file', e)
+
+def CopyBaseRevisionFile():
+ """Copy the BASE_REV_FILE from the WebKit checkout to the merge branch.
+
+ If options.real is False, log the copy but do not actually perform it.
+
+ Raises:
+ CommandError if the file copy fails.
+ """
+ logging.info('Copying %s to %s' % (BASE_REV_FILE, OldDir()))
+ if options.real:
+ try:
+ shutil.copyfile(NewDir(BASE_REV_FILE), OldDir(BASE_REV_FILE))
+ except IOError, e:
+ raise CommandError('Copying %s' % BASE_REV_FILE, e)
+
+
+def main(options, args):
+ """Perform the merge."""
+
+ ValidateArguments()
+
+ # Report configuration.
+ logging.info('Updating to revision %s' % options.revision)
+ logging.info('Updating into old directory %s' % options.old)
+ logging.info('Using WebKit from new directory %s' % options.new)
+ logging.info('Using temp directory %s' % options.temp_dir)
+
+ if not options.real:
+ logging.warning('DEBUGGING: Not issuing mkdir, cp, or gvn commands')
+
+ if options.svn_checkout:
+ RunSvnCheckout(options.revision)
+
+ UpdateBaseRevision(options.revision)
+
+ # Make sure desired directories are in the checkout.
+ for checkout_dir in options.dir_list:
+ if not os.path.exists(NewDir(checkout_dir)):
+ raise CannotFindError('webkit checkout dir', NewDir(checkout_dir))
+
+ # Generate or load diffs between relevant directories in the two trees.
+ saved_diff_path = TempDir(SAVE_EDITED_FILES)
+ if options.diff:
+ edit_files = RunDiff(saved_diff_path)
+ else:
+ logging.info('Reading diffs from %s' % saved_diff_path)
+ diff_text = open(saved_diff_path, 'r').read()
+ edit_files = set(diff_text.split('\n'))
+
+ # Collect filenames in each directory.
+ old_files = set()
+ new_files = set()
+ logging.info('Collecting lists of old and new files')
+ for collect_dir in options.dir_list:
+ logging.debug('Collecting old files in %s' % collect_dir)
+ old_files = old_files.union(CollectFileList(collect_dir, old=True))
+ logging.debug('Collecting new files in %s' % collect_dir)
+ new_files = new_files.union(CollectFileList(collect_dir, old=False))
+
+ add_files = new_files - old_files
+ delete_files = old_files - new_files
+
+ if DEBUGGING:
+ WriteFile(TempDir('old.txt'), '\n'.join(sorted(old_files)))
+ WriteFile(TempDir('new.txt'), '\n'.join(sorted(new_files)))
+
+ # Find any obsolete files ostensibly to be deleted, and rename (rather than
+ # re-adding) their corresponding new .h files. We trust that no filename
+ # that ends with '.obsolete' will also contain another '.obsolete' in its
+ # name.
+ obsolete_files = set()
+ # Iterate through a copy of the deleted set since we'll be changing it.
+ delete_files_copy = delete_files.copy()
+ for deleted in delete_files_copy:
+ if deleted.endswith('.obsolete'):
+ try:
+ # Only the add_files.remove() should be able to raise a KeyError.
+ # If we have no original file to rename, don't delete the .obsolete
+ # one either.
+ delete_files.remove(deleted)
+ deleted = deleted.replace('.obsolete', '')
+ add_files.remove(deleted)
+ obsolete_files.add(deleted)
+ except KeyError, e:
+ logging.warning('No new file found for old %s.obsolete.' % deleted)
+
+ # Save the list of obsolete files for future reference.
+ WriteFile(TempDir(SAVE_OBSOLETE_FILES), '\n'.join(sorted(obsolete_files)))
+
+ # Save the list of deleted files, converted to absolute paths, for use by
+ # gvn.
+ WriteAbsoluteFileList(TempDir(SAVE_DELETED_FILES), delete_files)
+
+ # Issue gvn deletes. We do this before copying over the added files so that
+ # an apparently new file that only differs from an old one in the case of
+ # its filename will be copied with the right name even though Windows
+ # filenames are case-insensitive. (Otherwise, if the file cppparser.h
+ # already existed in the Chrome branch but was renamed to CPPParser.h in the
+ # WebKit checkout, the "added" file CPPParser.h would be copied into
+ # cppparser.h rather than using its new name.)
+ if len(delete_files):
+ logging.info('Issuing gvn delete')
+ command = 'gvn --targets %s delete' % TempDir(SAVE_DELETED_FILES)
+ RunShellCommand(command, 'Running gvn delete', options.real)
+
+ # Copy added and edited files from the svn checkout, collecting added
+ # directories in the process.
+ if not options.deletes_only:
+ add_dirs = CopyFiles(add_files | edit_files)
+ for dir_name in add_dirs:
+ # We want relative paths, so truncate the path.
+ add_files.add(dir_name[len(OldDir()) + 1:])
+ CopyObsoleteFiles(obsolete_files)
+ CopyBaseRevisionFile()
+
+ # Save the sorted list of added files, converted to absolute paths, for use
+ # by gvn. Sorting ensures that new directories are added before their
+ # contents are.
+ WriteAbsoluteFileList(TempDir(SAVE_ADDED_FILES), add_files)
+
+ # Issue gvn adds.
+ os.chdir(OldDir())
+ if len(add_files) and not options.deletes_only:
+ logging.info('Issuing gvn add')
+ command = 'gvn --targets %s add' % TempDir(SAVE_ADDED_FILES)
+ RunShellCommand(command, 'Running gvn add', options.real)
+
+ # Print statistics.
+ logging.info('Edited: %s' % len(edit_files))
+ logging.info('Added: %s' % len(add_files))
+ logging.info('Deleted: %s' % len(delete_files))
+ logging.info('Renamed (obsoletes): %s' % len(obsolete_files))
+
+ return 0
+
+if '__main__' == __name__:
+ # Set up logging.
+ if DEBUGGING:
+ loglevel = logging.DEBUG
+ else:
+ loglevel = logging.INFO
+ logging.basicConfig(level=loglevel,
+ format='%(asctime)s %(levelname)-7s: %(message)s',
+ datefmt='%H:%M:%S')
+
+ # We need cygpath to convert our default paths.
+ try:
+ if not IsAvailable(CYGPATH):
+ raise CannotFindError('cygpath', CYGPATH)
+ except LocalError, e:
+ if e.stack_trace or DEBUGGING:
+ traceback.print_exc()
+ logging.error(e)
+ sys.exit(e.code)
+
+ # Set up option parsing.
+ opt = optparse.OptionParser()
+
+ opt.add_option('-r', '--revision', default=None,
+ help='(REQUIRED): New WebKit revision (already checked out, or to be)')
+
+ opt.add_option('', '--directories',
+ default='JavaScriptCore,WebCore',
+ help='Comma-separated list of directories to update')
+
+ opt.add_option('', '--old',
+ default=GetAbsolutePath('/cygdrive/c/src/merge/'),
+ help='Path to top of branch checkout, holding src/ and data/')
+
+ opt.add_option('', '--new',
+ default=GetAbsolutePath('/tmp/webkit-branch-merge/WebKit'),
+ help='Directory containing new WebKit checkout')
+
+ opt.add_option('', '--temp-dir',
+ default=GetAbsolutePath('/tmp/webkit-branch-merge'),
+ help='Temp directory available for use (will be created)')
+
+ opt.add_option('', '--svn-checkout', action='store_true',
+ default=False,
+ help='Run a new svn checkout into the new dir')
+
+ opt.add_option('', '--diff', action='store_true',
+ default=False,
+ help='Run a fresh diff rather than using a saved diff file')
+
+ opt.add_option('', '--deletes-only', action='store_true',
+ default=False,
+ help='Only issue gvn commands for deletes (this is useful'
+ 'when the case of a filename has changed)')
+
+ opt.add_option('', '--real', action='store_true',
+ default=False,
+ help="I'm ready: do the mkdir, cp, and gvn commands for real")
+
+ (options, args) = opt.parse_args()
+
+ # Run.
+ try:
+ exit_value = main(options, args)
+ except LocalError, e:
+ if e.stack_trace or DEBUGGING:
+ traceback.print_exc()
+ logging.error(e)
+ exit_value = e.code
+
+ sys.exit(exit_value)
diff --git a/webkit/tools/npapi_layout_test_plugin/PluginObject.cpp b/webkit/tools/npapi_layout_test_plugin/PluginObject.cpp
new file mode 100644
index 0000000..b012f58
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/PluginObject.cpp
@@ -0,0 +1,622 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#include "TestObject.h"
+#include <assert.h>
+#include <stdio.h>
+#ifdef WIN32
+#include <stdlib.h>
+#define snprintf sprintf_s
+#endif
+
+static void pluginInvalidate(NPObject *obj);
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
+static void pluginDeallocate(NPObject *obj);
+
+NPNetscapeFuncs *browser;
+
+static NPClass pluginClass = {
+ NP_CLASS_STRUCT_VERSION,
+ pluginAllocate,
+ pluginDeallocate,
+ pluginInvalidate,
+ pluginHasMethod,
+ pluginInvoke,
+ pluginInvokeDefault,
+ pluginHasProperty,
+ pluginGetProperty,
+ pluginSetProperty,
+};
+
+NPClass *getPluginClass(void)
+{
+ return &pluginClass;
+}
+
+static bool identifiersInitialized = false;
+
+#define ID_PROPERTY_PROPERTY 0
+#define ID_PROPERTY_EVENT_LOGGING 1
+#define ID_PROPERTY_HAS_STREAM 2
+#define ID_PROPERTY_TEST_OBJECT 3
+#define ID_PROPERTY_LOG_DESTROY 4
+#define ID_PROPERTY_TEST_OBJECT_COUNT 5
+#define NUM_PROPERTY_IDENTIFIERS 6
+
+static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
+static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
+ "property",
+ "eventLoggingEnabled",
+ "hasStream",
+ "testObject",
+ "logDestroy",
+ "testObjectCount",
+};
+
+#define ID_TEST_CALLBACK_METHOD 0
+#define ID_TEST_GETURL 1
+#define ID_REMOVE_DEFAULT_METHOD 2
+#define ID_TEST_DOM_ACCESS 3
+#define ID_TEST_GET_URL_NOTIFY 4
+#define ID_TEST_INVOKE_DEFAULT 5
+#define ID_DESTROY_STREAM 6
+#define ID_TEST_ENUMERATE 7
+#define ID_TEST_GETINTIDENTIFIER 8
+#define ID_TEST_GET_PROPERTY 9
+#define ID_TEST_EVALUATE 10
+#define ID_TEST_GET_PROPERTY_RETURN_VALUE 11
+#define ID_TEST_CALLBACK_METHOD_RET 12
+#define ID_TEST_CREATE_TEST_OBJECT 13
+#define ID_TEST_PASS_TEST_OBJECT 14
+#define ID_TEST_CLONE_OBJECT 15
+#define ID_TEST_SCRIPT_OBJECT_INVOKE 16
+#define NUM_METHOD_IDENTIFIERS 17
+
+static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "testCallback",
+ "getURL",
+ "removeDefaultMethod",
+ "testDOMAccess",
+ "getURLNotify",
+ "testInvokeDefault",
+ "destroyStream",
+ "testEnumerate",
+ "testGetIntIdentifier",
+ "testGetProperty",
+ "testEvaluate",
+ "testGetPropertyReturnValue",
+ "testCallbackRet", // Chrome bug 897451
+ "testCreateTestObject", // Chrome bug 1093606
+ "testPassTestObject", // Chrome bug 1093606
+ "testCloneObject",
+ "testScriptObjectInvoke", // Chrome bug 1175346
+};
+
+static NPUTF8* createCStringFromNPVariant(const NPVariant *variant)
+{
+ size_t length = NPVARIANT_TO_STRING(*variant).UTF8Length;
+ NPUTF8* result = (NPUTF8*)malloc(length + 1);
+ memcpy(result, NPVARIANT_TO_STRING(*variant).UTF8Characters, length);
+ result[length] = '\0';
+ return result;
+}
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
+ browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
+}
+
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
+{
+ for (int i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
+ if (name == pluginPropertyIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
+{
+ for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
+ if (name == pluginMethodIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
+{
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_PROPERTY]) {
+ char* mem = static_cast<char*>(browser->memalloc(9));
+ strcpy(mem, "property");
+ STRINGZ_TO_NPVARIANT(mem, *variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
+ BOOLEAN_TO_NPVARIANT(((PluginObject *)obj)->eventLogging, *variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
+ BOOLEAN_TO_NPVARIANT(((PluginObject *)obj)->logDestroy, *variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_HAS_STREAM]) {
+ BOOLEAN_TO_NPVARIANT(((PluginObject *)obj)->stream != 0, *variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
+ NPObject *testObject = ((PluginObject *)obj)->testObject;
+ browser->retainobject(testObject);
+ OBJECT_TO_NPVARIANT(testObject, *variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT_COUNT]) {
+ INT32_TO_NPVARIANT(getTestObjectCount(), *variant);
+ return true;
+ }
+ return false;
+}
+
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
+{
+ if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
+ ((PluginObject *)obj)->eventLogging = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
+ ((PluginObject *)obj)->logDestroy = NPVARIANT_TO_BOOLEAN(*variant);
+ return true;
+ }
+
+ return false;
+}
+
+static void testDOMAccess(PluginObject *obj)
+{
+ // Get plug-in's DOM element
+ NPObject *elementObject;
+ if (browser->getvalue(obj->npp, NPNVPluginElementNPObject, &elementObject) == NPERR_NO_ERROR) {
+ // Get style
+ NPVariant styleVariant;
+ NPIdentifier styleIdentifier = browser->getstringidentifier("style");
+ if (browser->getproperty(obj->npp, elementObject, styleIdentifier, &styleVariant) && NPVARIANT_IS_OBJECT(styleVariant)) {
+ // Set style.border
+ NPIdentifier borderIdentifier = browser->getstringidentifier("border");
+ NPVariant borderVariant;
+ STRINGZ_TO_NPVARIANT("3px solid red", borderVariant);
+ browser->setproperty(obj->npp, NPVARIANT_TO_OBJECT(styleVariant), borderIdentifier, &borderVariant);
+ browser->releasevariantvalue(&styleVariant);
+ }
+
+ browser->releaseobject(elementObject);
+ }
+}
+
+static bool pluginInvoke(NPObject *header, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ PluginObject *obj = (PluginObject *)header;
+ if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD]) {
+ // call whatever method name we're given
+ if (argCount > 0 && NPVARIANT_IS_STRING(args[0])) {
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant browserResult;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_TEST_GETURL]) {
+ if (argCount == 2 && NPVARIANT_IS_STRING(args[0]) && NPVARIANT_IS_STRING(args[1])) {
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ NPUTF8* targetString = createCStringFromNPVariant(&args[1]);
+ browser->geturl(obj->npp, urlString, targetString);
+ free(urlString);
+ free(targetString);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ } else if (argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ browser->geturl(obj->npp, urlString, 0);
+ free(urlString);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_REMOVE_DEFAULT_METHOD]) {
+ pluginClass.invokeDefault = 0;
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_DOM_ACCESS]) {
+ testDOMAccess(obj);
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_GET_URL_NOTIFY]) {
+ if (argCount == 3
+ && NPVARIANT_IS_STRING(args[0])
+ && (NPVARIANT_IS_STRING(args[1]) || NPVARIANT_IS_NULL(args[1]))
+ && NPVARIANT_IS_STRING(args[2])) {
+ NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
+ NPUTF8* targetString = (NPVARIANT_IS_STRING(args[1]) ? createCStringFromNPVariant(&args[1]) : NULL);
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[2]);
+
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ browser->geturlnotify(obj->npp, urlString, targetString, callbackIdentifier);
+
+ free(urlString);
+ free(targetString);
+ free(callbackString);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_TEST_INVOKE_DEFAULT] && NPVARIANT_IS_OBJECT(args[0])) {
+ NPObject *callback = NPVARIANT_TO_OBJECT(args[0]);
+
+ NPVariant args[1];
+ NPVariant browserResult;
+
+ STRINGZ_TO_NPVARIANT("test", args[0]);
+ bool retval = browser->invokeDefault(obj->npp, callback, args, 1, &browserResult);
+
+ if (retval)
+ browser->releasevariantvalue(&browserResult);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_ENUMERATE]) {
+ if (argCount == 2 && NPVARIANT_IS_OBJECT(args[0]) && NPVARIANT_IS_OBJECT(args[1])) {
+ uint32_t count;
+ NPIdentifier* identifiers;
+
+ if (browser->enumerate(obj->npp, NPVARIANT_TO_OBJECT(args[0]), &identifiers, &count)) {
+ NPObject* outArray = NPVARIANT_TO_OBJECT(args[1]);
+ NPIdentifier pushIdentifier = browser->getstringidentifier("push");
+
+ for (uint32_t i = 0; i < count; i++) {
+ NPUTF8* string = browser->utf8fromidentifier(identifiers[i]);
+
+ if (!string)
+ continue;
+
+ NPVariant args[1];
+ STRINGZ_TO_NPVARIANT(string, args[0]);
+ NPVariant browserResult;
+ browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+ browser->memfree(string);
+ }
+
+ browser->memfree(identifiers);
+ }
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ return false;
+ } else if (name == pluginMethodIdentifiers[ID_DESTROY_STREAM]) {
+ NPError npError = browser->destroystream(obj->npp, obj->stream, NPRES_USER_BREAK);
+ INT32_TO_NPVARIANT(npError, *result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_GETINTIDENTIFIER]) {
+ if (argCount == 1 && NPVARIANT_IS_DOUBLE(args[0])) {
+ NPIdentifier identifier = browser->getintidentifier((int)NPVARIANT_TO_DOUBLE(args[0]));
+ INT32_TO_NPVARIANT((int32)identifier, *result);
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_TEST_EVALUATE] &&
+ argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPString s = NPVARIANT_TO_STRING(args[0]);
+
+ bool retval = browser->evaluate(obj->npp, windowScriptObject, &s, result);
+ browser->releaseobject(windowScriptObject);
+ return retval;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY] &&
+ argCount > 0) {
+ NPObject *object;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &object);
+
+ for (uint32_t i = 0; i < argCount; i++) {
+ assert(NPVARIANT_IS_STRING(args[i]));
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[i]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ NPVariant variant;
+ bool retval = browser->getproperty(obj->npp, object, propertyIdentifier, &variant);
+ browser->releaseobject(object);
+
+ if (!retval)
+ break;
+
+ if (i + 1 < argCount) {
+ assert(NPVARIANT_IS_OBJECT(variant));
+ object = NPVARIANT_TO_OBJECT(variant);
+ } else {
+ *result = variant;
+ return true;
+ }
+ }
+
+ VOID_TO_NPVARIANT(*result);
+ return false;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY_RETURN_VALUE] &&
+ argCount == 2 && NPVARIANT_IS_OBJECT(args[0]) && NPVARIANT_IS_STRING(args[1])) {
+ NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
+ NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
+ free(propertyString);
+
+ NPVariant variant;
+ bool retval = browser->getproperty(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier, &variant);
+ if (retval)
+ browser->releasevariantvalue(&variant);
+
+ BOOLEAN_TO_NPVARIANT(retval, *result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD_RET]) {
+ // call whatever method name we're given, and pass it the 'window' obj.
+ // we expect the function to return its argument.
+ if (argCount > 0 && NPVARIANT_IS_STRING(args[0])) {
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant callbackArgs[1];
+ OBJECT_TO_NPVARIANT(windowScriptObject, callbackArgs[0]);
+
+ NPVariant browserResult;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier,
+ callbackArgs, 1, &browserResult);
+
+ if (NPVARIANT_IS_OBJECT(browserResult)) {
+ // Now return the callbacks return value back to our caller.
+ // BUG 897451: This should be the same as the
+ // windowScriptObject, but its not (in Chrome) - or at least, it
+ // has a different refcount. This means Chrome will delete the
+ // object before returning it and the calling JS gets a garbage
+ // value. Firefox handles it fine.
+ OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(browserResult), *result);
+ } else {
+ browser->releasevariantvalue(&browserResult);
+ VOID_TO_NPVARIANT(*result);
+ }
+
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_TEST_CREATE_TEST_OBJECT]) {
+ NPObject *testObject = browser->createobject(obj->npp, getTestClass());
+ assert(testObject->referenceCount == 1);
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_PASS_TEST_OBJECT]) {
+ // call whatever method name we're given, and pass it our second
+ // argument.
+ if (argCount > 1 && NPVARIANT_IS_STRING(args[0])) {
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ NPVariant browserResult;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, &args[1], 1, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+ }
+ } else if (name == pluginMethodIdentifiers[ID_TEST_CLONE_OBJECT]) {
+ // Create another instance of the same class
+ NPObject *new_object = browser->createobject(obj->npp, &pluginClass);
+ assert(new_object->referenceCount == 1);
+ OBJECT_TO_NPVARIANT(new_object, *result);
+ return true;
+ } else if (name == pluginMethodIdentifiers[ID_TEST_SCRIPT_OBJECT_INVOKE]) {
+ if (argCount > 1 && NPVARIANT_IS_STRING(args[0])) {
+ // Invoke a script callback to get a script NPObject. Then call
+ // a method on the script NPObject passing it a freshly created
+ // NPObject.
+ // Arguments:
+ // arg1: Callback that returns a script object.
+ // arg2: Name of the method to call on the script object returned
+ // from the callback
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject,
+ &windowScriptObject);
+
+ // Arg1 is the name of the callback
+ NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
+ NPIdentifier callbackIdentifier =
+ browser->getstringidentifier(callbackString);
+ free(callbackString);
+
+ // Invoke a callback that returns a script object
+ NPVariant object_result;
+ browser->invoke(obj->npp, windowScriptObject, callbackIdentifier,
+ &args[1], 1, &object_result);
+
+ // Script object returned
+ NPObject *script_object = object_result.value.objectValue;
+
+ // Arg2 is the name of the method to be called on the script object
+ NPUTF8* object_mehod_string = createCStringFromNPVariant(&args[1]);
+ NPIdentifier object_method =
+ browser->getstringidentifier(object_mehod_string);
+ free(object_mehod_string);
+
+ // Create a fresh NPObject to be passed as an argument
+ NPObject *object_arg = browser->createobject(obj->npp, &pluginClass);
+ NPVariant invoke_args[1];
+ OBJECT_TO_NPVARIANT(object_arg, invoke_args[0]);
+
+ // Invoke the script method
+ NPVariant object_method_result;
+ browser->invoke(obj->npp, script_object, object_method,
+ invoke_args, 1, &object_method_result);
+
+ browser->releasevariantvalue(&object_result);
+ VOID_TO_NPVARIANT(*result);
+ if (NPVARIANT_IS_OBJECT(object_method_result)) {
+ // Now return the callbacks return value back to our caller.
+ // BUG 897451: This should be the same as the
+ // windowScriptObject, but its not (in Chrome) - or at least, it
+ // has a different refcount. This means Chrome will delete the
+ // object before returning it and the calling JS gets a garbage
+ // value. Firefox handles it fine.
+ OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(object_method_result),
+ *result);
+ } else {
+ browser->releasevariantvalue(&object_method_result);
+ VOID_TO_NPVARIANT(*result);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ INT32_TO_NPVARIANT(1, *result);
+ return true;
+}
+
+static void pluginInvalidate(NPObject *obj)
+{
+}
+
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
+{
+ PluginObject *newInstance = (PluginObject*)malloc(sizeof(PluginObject));
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ newInstance->npp = npp;
+ newInstance->testObject = browser->createobject(npp, getTestClass());
+ newInstance->eventLogging = FALSE;
+ newInstance->logDestroy = FALSE;
+ newInstance->logSetWindow = FALSE;
+ newInstance->returnErrorFromNewStream = FALSE;
+ newInstance->stream = 0;
+
+ newInstance->firstUrl = NULL;
+ newInstance->firstHeaders = NULL;
+ newInstance->lastUrl = NULL;
+ newInstance->lastHeaders = NULL;
+
+ return (NPObject *)newInstance;
+}
+
+static void pluginDeallocate(NPObject *header)
+{
+ PluginObject* obj = (PluginObject*)header;
+
+ browser->releaseobject(obj->testObject);
+
+ free(obj->firstUrl);
+ free(obj->firstHeaders);
+ free(obj->lastUrl);
+ free(obj->lastHeaders);
+
+ free(obj);
+}
+
+void handleCallback(PluginObject* object, const char *url, NPReason reason, void *notifyData)
+{
+ assert(object);
+
+ NPVariant args[2];
+
+ NPObject *windowScriptObject;
+ browser->getvalue(object->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPIdentifier callbackIdentifier = notifyData;
+
+ INT32_TO_NPVARIANT(reason, args[0]);
+
+ char *strHdr = NULL;
+ if (object->firstUrl && object->firstHeaders && object->lastUrl && object->lastHeaders) {
+ // Format expected by JavaScript validator: four fields separated by \n\n:
+ // First URL; first header block; last URL; last header block.
+ // Note that header blocks already end with \n due to how NPStream::headers works.
+ int len = strlen(object->firstUrl) + 2
+ + strlen(object->firstHeaders) + 1
+ + strlen(object->lastUrl) + 2
+ + strlen(object->lastHeaders) + 1;
+ strHdr = (char*)malloc(len + 1);
+ snprintf(strHdr, len + 1, "%s\n\n%s\n%s\n\n%s\n",
+ object->firstUrl, object->firstHeaders, object->lastUrl, object->lastHeaders);
+ STRINGN_TO_NPVARIANT(strHdr, len, args[1]);
+ } else
+ NULL_TO_NPVARIANT(args[1]);
+
+ NPVariant browserResult;
+ browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+
+ free(strHdr);
+}
+
+void notifyStream(PluginObject* object, const char *url, const char *headers)
+{
+ if (object->firstUrl == NULL) {
+ if (url)
+ object->firstUrl = strdup(url);
+ if (headers)
+ object->firstHeaders = strdup(headers);
+ } else {
+ free(object->lastUrl);
+ free(object->lastHeaders);
+ object->lastUrl = (url ? strdup(url) : NULL);
+ object->lastHeaders = (headers ? strdup(headers) : NULL);
+ }
+}
diff --git a/webkit/tools/npapi_layout_test_plugin/PluginObject.h b/webkit/tools/npapi_layout_test_plugin/PluginObject.h
new file mode 100644
index 0000000..cacfe41
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/PluginObject.h
@@ -0,0 +1,56 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "webkit/glue/plugins/nphostapi.h"
+
+extern NPNetscapeFuncs *browser;
+
+typedef struct {
+ NPObject header;
+ NPP npp;
+ NPBool eventLogging;
+ NPBool logSetWindow;
+ NPBool logDestroy;
+ NPBool returnErrorFromNewStream;
+ NPObject* testObject;
+ NPStream* stream;
+ char* onStreamLoad;
+ char* firstUrl;
+ char* firstHeaders;
+ char* lastUrl;
+ char* lastHeaders;
+} PluginObject;
+
+extern NPClass *getPluginClass(void);
+extern void handleCallback(PluginObject* object, const char *url, NPReason reason, void *notifyData);
+extern void notifyStream(PluginObject* object, const char *url, const char *headers);
diff --git a/webkit/tools/npapi_layout_test_plugin/SConscript b/webkit/tools/npapi_layout_test_plugin/SConscript
new file mode 100644
index 0000000..3536bd3
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/SConscript
@@ -0,0 +1,100 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env', 'env_res')
+
+env = env.Clone()
+env_res = env_res.Clone()
+
+input_files = [
+ 'main.cpp',
+ env_res.RES('npapi_layout_test_plugin.rc'),
+ 'PluginObject.cpp',
+ 'TestObject.cpp',
+ 'npapi_layout_test_plugin.def',
+]
+
+env.Append(
+ CCFLAGS = [
+ '/TP',
+ '/WX',
+ '/wd4503',
+ '/wd4819',
+ ],
+
+ LIBS = [
+ 'advapi32.lib',
+ 'comctl32.lib',
+ 'comdlg32.lib',
+ 'delayimp.lib',
+ 'gdi32.lib',
+ 'kernel32.lib',
+ 'msimg32.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'psapi.lib',
+ 'rpcrt4.lib',
+ 'shell32.lib',
+ 'shlwapi.lib',
+ 'user32.lib',
+ 'usp10.lib',
+ 'uuid.lib',
+ 'version.lib',
+ 'wininet.lib',
+ 'winmm.lib',
+ 'winspool.lib',
+ 'ws2_32.lib',
+ ],
+
+ LINKFLAGS = [
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+ '/FIXED:No',
+ '/SUBSYSTEM:CONSOLE',
+ '/MACHINE:X86',
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+)
+
+dll = env.SharedLibrary(['npapi_layout_test_plugin',
+ 'npapi_layout_test_plugin.lib',
+ 'npapi_layout_test_plugin.ilk',
+ 'npapi_layout_test_plugin.pdb'],
+ input_files)
+
+i = env.Install('$TARGET_ROOT/plugins', dll)
+env.Alias('webkit', i)
+
+i = env.Install('$TARGET_ROOT', dll)
+env.Alias('webkit', i)
diff --git a/webkit/tools/npapi_layout_test_plugin/TestObject.cpp b/webkit/tools/npapi_layout_test_plugin/TestObject.cpp
new file mode 100644
index 0000000..141061d2
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/TestObject.cpp
@@ -0,0 +1,169 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "TestObject.h"
+#include "PluginObject.h"
+
+#include <stdlib.h>
+
+static bool testEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+static bool testInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static bool testHasProperty(NPObject *obj, NPIdentifier name);
+static bool testGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
+static NPObject *testAllocate(NPP npp, NPClass *theClass);
+static void testDeallocate(NPObject *obj);
+
+static NPClass testClass = {
+ NP_CLASS_STRUCT_VERSION,
+ testAllocate,
+ testDeallocate,
+ 0,
+ 0,
+ 0,
+ testInvokeDefault,
+ testHasProperty,
+ testGetProperty,
+ 0,
+ 0,
+ testEnumerate
+};
+
+NPClass *getTestClass(void)
+{
+ return &testClass;
+}
+
+int testObjectCount = 0;
+
+int getTestObjectCount(void) {
+ return testObjectCount;
+}
+
+static bool identifiersInitialized = false;
+
+#define NUM_TEST_IDENTIFIERS 4
+#define ID_PROPERTY_FOO 0
+#define ID_PROPERTY_BAR 1
+#define ID_PROPERTY_TEST_OBJECT 2
+#define ID_PROPERTY_REF_COUNT 3
+
+static NPIdentifier testIdentifiers[NUM_TEST_IDENTIFIERS];
+static const NPUTF8 *testIdentifierNames[NUM_TEST_IDENTIFIERS] = {
+ "foo",
+ "bar",
+ "testObject",
+ "refCount",
+};
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(testIdentifierNames, NUM_TEST_IDENTIFIERS, testIdentifiers);
+}
+
+static NPObject *testAllocate(NPP npp, NPClass *theClass)
+{
+ TestObject *newInstance =
+ static_cast<TestObject*>(malloc(sizeof(TestObject)));
+ newInstance->testObject = NULL;
+ ++testObjectCount;
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ return reinterpret_cast<NPObject*>(newInstance);
+}
+
+static void testDeallocate(NPObject *obj)
+{
+ TestObject *testObject = reinterpret_cast<TestObject*>(obj);
+ if (testObject->testObject)
+ browser->releaseobject(testObject->testObject);
+ --testObjectCount;
+ free(obj);
+}
+
+static bool testInvokeDefault(NPObject *obj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ INT32_TO_NPVARIANT(2, *result);
+ return true;
+}
+
+static bool testHasProperty(NPObject *obj, NPIdentifier name)
+{
+ for (unsigned i = 0; i < NUM_TEST_IDENTIFIERS; i++) {
+ if (testIdentifiers[i] == name)
+ return true;
+ }
+
+ return false;
+}
+
+static bool testGetProperty(NPObject *obj, NPIdentifier name,
+ NPVariant *variant)
+{
+ if (name == testIdentifiers[ID_PROPERTY_FOO]) {
+ char* mem = static_cast<char*>(browser->memalloc(4));
+ strcpy(mem, "foo");
+ STRINGZ_TO_NPVARIANT(mem, *variant);
+ return true;
+ } else if (name == testIdentifiers[ID_PROPERTY_BAR]) {
+ BOOLEAN_TO_NPVARIANT(true, *variant);
+ return true;
+ } else if (name == testIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
+ TestObject* testObject = reinterpret_cast<TestObject*>(obj);
+ if (testObject->testObject == NULL)
+ testObject->testObject = browser->createobject(NULL, &testClass);
+ browser->retainobject(testObject->testObject);
+ OBJECT_TO_NPVARIANT(testObject->testObject, *variant);
+ return true;
+ } else if (name == testIdentifiers[ID_PROPERTY_REF_COUNT]) {
+ INT32_TO_NPVARIANT(obj->referenceCount, *variant);
+ return true;
+ }
+ return false;
+}
+
+static bool testEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
+{
+ *count = NUM_TEST_IDENTIFIERS;
+
+ *value = (NPIdentifier*)browser->memalloc(NUM_TEST_IDENTIFIERS * sizeof(NPIdentifier));
+ memcpy(*value, testIdentifiers, sizeof(NPIdentifier) * NUM_TEST_IDENTIFIERS);
+
+ return true;
+}
+
+
diff --git a/webkit/tools/npapi_layout_test_plugin/TestObject.h b/webkit/tools/npapi_layout_test_plugin/TestObject.h
new file mode 100644
index 0000000..fe9839f
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/TestObject.h
@@ -0,0 +1,44 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bindings/npapi.h"
+#include "bindings/npruntime.h"
+
+
+typedef struct {
+ NPObject header;
+ NPObject* testObject;
+} TestObject;
+
+NPClass *getTestClass(void);
+int getTestObjectCount(void); \ No newline at end of file
diff --git a/webkit/tools/npapi_layout_test_plugin/main.cpp b/webkit/tools/npapi_layout_test_plugin/main.cpp
new file mode 100644
index 0000000..0ad2d6b
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/main.cpp
@@ -0,0 +1,346 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#ifdef WIN32
+#include <stdio.h>
+#include <stdlib.h>
+#define strcasecmp _stricmp
+#define NPAPI WINAPI
+#else
+#define NPAPI
+#endif
+
+// Mach-o entry points
+extern "C" {
+ NPError NPAPI NP_Initialize(NPNetscapeFuncs *browserFuncs);
+ NPError NPAPI NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
+ void NPAPI NP_Shutdown(void);
+}
+
+// Mach-o entry points
+NPError NPAPI NP_Initialize(NPNetscapeFuncs *browserFuncs)
+{
+ browser = browserFuncs;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPAPI NP_GetEntryPoints(NPPluginFuncs *pluginFuncs)
+{
+ pluginFuncs->version = 11;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ return NPERR_NO_ERROR;
+}
+
+void NPAPI NP_Shutdown(void)
+{
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char *argn[], char *argv[], NPSavedData *saved)
+{
+ if (browser->version >= 14) {
+ PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass());
+
+ obj->onStreamLoad = NULL;
+
+ for (int i = 0; i < argc; i++) {
+ if (strcasecmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad)
+ obj->onStreamLoad = strdup(argv[i]);
+ else if (strcasecmp(argn[i], "src") == 0 &&
+ strcasecmp(argv[i], "data:application/x-webkit-test-netscape,returnerrorfromnewstream") == 0)
+ obj->returnErrorFromNewStream = TRUE;
+ else if (strcasecmp(argn[i], "logfirstsetwindow") == 0)
+ obj->logSetWindow = TRUE;
+ }
+
+ instance->pdata = obj;
+ }
+
+ // On Windows and Unix, plugins only get events if they are windowless.
+ return browser->setvalue(instance, NPPVpluginWindowBool, NULL);
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData **save)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ if (obj) {
+ if (obj->onStreamLoad)
+ free(obj->onStreamLoad);
+
+ if (obj->logDestroy)
+ printf("PLUGIN: NPP_Destroy\n");
+
+ browser->releaseobject(&obj->header);
+ }
+
+ fflush(stdout);
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow *window)
+{
+ if (window->window == NULL) {
+ return NPERR_NO_ERROR;
+ }
+
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+
+ if (obj) {
+ if (obj->logSetWindow) {
+ printf("PLUGIN: NPP_SetWindow: %d %d\n", (int)window->width, (int)window->height);
+ obj->logSetWindow = false;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ obj->stream = stream;
+ *stype = NP_ASFILEONLY;
+
+ if (obj->returnErrorFromNewStream)
+ return NPERR_GENERIC_ERROR;
+
+ if (browser->version >= NPVERS_HAS_RESPONSE_HEADERS)
+ notifyStream(obj, stream->url, stream->headers);
+
+ if (obj->onStreamLoad) {
+ NPObject *windowScriptObject;
+ browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
+
+ NPString script;
+ script.UTF8Characters = obj->onStreamLoad;
+ script.UTF8Length = strlen(obj->onStreamLoad);
+
+ NPVariant browserResult;
+ browser->evaluate(obj->npp, windowScriptObject, &script, &browserResult);
+ browser->releasevariantvalue(&browserResult);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ obj->stream = 0;
+
+ return NPERR_NO_ERROR;
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream *stream)
+{
+ return 0;
+}
+
+int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
+{
+ return 0;
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint *platformPrint)
+{
+}
+
+int16 NPP_HandleEvent(NPP instance, void *event)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ if (!obj->eventLogging)
+ return 0;
+
+#ifdef WIN32
+ // Below is the event handling code. Per the NPAPI spec, the events don't
+ // map directly between operating systems:
+ // http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000
+ NPEvent* evt = static_cast<NPEvent*>(event);
+ short x = static_cast<short>(evt->lParam & 0xffff);
+ short y = static_cast<short>(evt->lParam >> 16);
+ switch (evt->event) {
+ case WM_PAINT:
+ printf("PLUGIN: updateEvt\n");
+ break;
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ printf("PLUGIN: mouseDown at (%d, %d)\n", x, y);
+ break;
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ printf("PLUGIN: mouseUp at (%d, %d)\n", x, y);
+ break;
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ break;
+ case WM_MOUSEMOVE:
+ printf("PLUGIN: adjustCursorEvent\n");
+ break;
+ case WM_KEYUP:
+ // TODO(tc): We need to convert evt->wParam from virtual-key code
+ // to key code.
+ printf("NOTIMPLEMENTED PLUGIN: keyUp '%c'\n", ' ');
+ break;
+ case WM_KEYDOWN:
+ // TODO(tc): We need to convert evt->wParam from virtual-key code
+ // to key code.
+ printf("NOTIMPLEMENTED PLUGIN: keyDown '%c'\n", ' ');
+ break;
+ case WM_SETCURSOR:
+ break;
+ case WM_SETFOCUS:
+ printf("PLUGIN: getFocusEvent\n");
+ break;
+ case WM_KILLFOCUS:
+ printf("PLUGIN: loseFocusEvent\n");
+ break;
+ default:
+ printf("PLUGIN: event %d\n", evt->event);
+ }
+
+ fflush(stdout);
+
+#else
+ EventRecord* evt = static_cast<EventRecord*>(event);
+ Point pt = { evt->where.v, evt->where.h };
+ switch (evt->what) {
+ case nullEvent:
+ // these are delivered non-deterministically, don't log.
+ break;
+ case mouseDown:
+ GlobalToLocal(&pt);
+ printf("PLUGIN: mouseDown at (%d, %d)\n", pt.h, pt.v);
+ break;
+ case mouseUp:
+ GlobalToLocal(&pt);
+ printf("PLUGIN: mouseUp at (%d, %d)\n", pt.h, pt.v);
+ break;
+ case keyDown:
+ printf("PLUGIN: keyDown '%c'\n", (char)(evt->message & 0xFF));
+ break;
+ case keyUp:
+ printf("PLUGIN: keyUp '%c'\n", (char)(evt->message & 0xFF));
+ break;
+ case autoKey:
+ printf("PLUGIN: autoKey '%c'\n", (char)(evt->message & 0xFF));
+ break;
+ case updateEvt:
+ printf("PLUGIN: updateEvt\n");
+ break;
+ case diskEvt:
+ printf("PLUGIN: diskEvt\n");
+ break;
+ case activateEvt:
+ printf("PLUGIN: activateEvt\n");
+ break;
+ case osEvt:
+ printf("PLUGIN: osEvt - ");
+ switch ((evt->message & 0xFF000000) >> 24) {
+ case suspendResumeMessage:
+ printf("%s\n", (evt->message & 0x1) ? "resume" : "suspend");
+ break;
+ case mouseMovedMessage:
+ printf("mouseMoved\n");
+ break;
+ default:
+ printf("%08lX\n", evt->message);
+ }
+ break;
+ case kHighLevelEvent:
+ printf("PLUGIN: kHighLevelEvent\n");
+ break;
+ // NPAPI events
+ case getFocusEvent:
+ printf("PLUGIN: getFocusEvent\n");
+ break;
+ case loseFocusEvent:
+ printf("PLUGIN: loseFocusEvent\n");
+ break;
+ case adjustCursorEvent:
+ printf("PLUGIN: adjustCursorEvent\n");
+ break;
+ default:
+ printf("PLUGIN: event %d\n", evt->what);
+ }
+#endif
+
+ return 0;
+}
+
+void NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData)
+{
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+
+ handleCallback(obj, url, reason, notifyData);
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+ if (variable == NPPVpluginScriptableNPObject) {
+ void **v = (void **)value;
+ PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
+ // Return value is expected to be retained
+ browser->retainobject((NPObject *)obj);
+ *v = obj;
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ return NPERR_GENERIC_ERROR;
+}
diff --git a/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.def b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.def
new file mode 100644
index 0000000..dff1e6a
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.def
@@ -0,0 +1,6 @@
+LIBRARY npapi_layout_test_plugin
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
diff --git a/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.rc b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.rc
new file mode 100644
index 0000000..080b7a8
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.rc
@@ -0,0 +1,104 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "FileDescription", "Simple Netscape plug-in that handles test content for WebKit"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "npapi_te"
+ VALUE "LegalCopyright", "Copyright (C) 2007"
+ VALUE "MIMEType", "application/x-webkit-test-netscape"
+ VALUE "FileExtents", "testnetscape"
+ VALUE "FileOpenName", "test netscape content"
+ VALUE "OriginalFilename", "npapi_te.dll"
+ VALUE "ProductName", "WebKit Test PlugIn"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.vcproj b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.vcproj
new file mode 100644
index 0000000..ffcd474
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/npapi_layout_test_plugin.vcproj
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="npapi_layout_test_plugin"
+ ProjectGUID="{BE6D5659-A8D5-4890-A42C-090DD10EF62C}"
+ RootNamespace="npapi_layout_test_plugin"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\..\build\webkit_common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ ModuleDefinitionFile="npapi_layout_test_plugin.def"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="xcopy.exe /Y /F $(TargetPath) $(TargetDir)\plugins\"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;..\..\build\webkit_common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ ModuleDefinitionFile="npapi_layout_test_plugin.def"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="xcopy.exe /Y /F $(TargetPath) $(TargetDir)\plugins\"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\npapi_layout_test_plugin.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\PluginObject.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\PluginObject.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\TestObject.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\TestObject.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/webkit/tools/npapi_layout_test_plugin/resource.h b/webkit/tools/npapi_layout_test_plugin/resource.h
new file mode 100644
index 0000000..c2bcf92
--- /dev/null
+++ b/webkit/tools/npapi_layout_test_plugin/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by npapi_layout_test_plugin.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/webkit/tools/test_shell/SConscript b/webkit/tools/test_shell/SConscript
new file mode 100644
index 0000000..3509a0c
--- /dev/null
+++ b/webkit/tools/test_shell/SConscript
@@ -0,0 +1,227 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env', 'env_res')
+
+env = env.Clone()
+env_res = env_res.Clone()
+
+env_res.Append(
+ CPPPATH = [
+ '.',
+ '#/..',
+ '$NET_DIR',
+ ],
+ RCFLAGS = [
+ ['/l', '0x409'],
+ ],
+)
+
+input_files = [
+ 'drag_delegate.cc',
+ 'drop_delegate.cc',
+ 'event_sending_controller.cc',
+ 'layout_test_controller.cc',
+ 'simple_resource_loader_bridge.cc',
+ 'test_navigation_controller.cc',
+ 'test_shell.cc',
+ 'test_shell_switches.cc',
+ 'test_shell_request_context.cc',
+ 'test_webview_delegate.cc',
+ 'text_input_controller.cc',
+ 'webview_host.cc',
+ 'webwidget_host.cc',
+ 'temp/navigation_controller_base.cc',
+]
+
+env.Append(
+ CPPPATH = [
+ '$BREAKPAD_DIR/src',
+ '$WEBKIT_DIR/glue',
+ '$GTEST_DIR/include',
+ ],
+ CCFLAGS = [
+ '/TP',
+ '/WX',
+ '/Wp64',
+ '/wd4503',
+ '/wd4819',
+ ],
+
+ LIBS = [
+ 'comctl32.lib',
+ 'shlwapi.lib',
+ 'rpcrt4.lib',
+ 'winmm.lib',
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+
+ 'delayimp.lib',
+ ],
+
+ LINKFLAGS = [
+ '/DELAYLOAD:"ws2_32.dll"',
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+ '/FIXED:No',
+ '/SUBSYSTEM:CONSOLE',
+ '/MACHINE:X86',
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+)
+
+lib = env.Library('test_shell', input_files)
+
+
+
+
+resources = [
+ env_res.RES('resources/test_shell.rc'),
+ '$NET_DIR/net_resources.res',
+ '$WEBKIT_DIR/build/localized_strings/webkit_strings_en-US.res',
+]
+
+components = [
+ lib,
+
+ '$BASE_DIR/base.lib',
+ '$BASE_DIR/gfx/base_gfx.lib',
+ '$BREAKPAD_DIR/breakpad_handler.lib',
+ '$BREAKPAD_DIR/breakpad_sender.lib',
+ '$GOOGLEURL_DIR/googleurl.lib',
+ '$NET_DIR/net.lib',
+ '$SKIA_DIR/skia.lib',
+ '$TESTING_DIR/gtest.lib',
+ '$BZIP2_DIR/bzip2.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$LIBJPEG_DIR/libjpeg.lib',
+ '$LIBPNG_DIR/libpng.lib',
+ '$LIBXML_DIR/libxml.lib',
+ '$LIBXSLT_DIR/libxslt.lib',
+ '$MODP_B64_DIR/modp_b64.lib',
+ '$ZLIB_DIR/zlib.lib',
+ '$V8_DIR/v8.lib',
+ '$V8_DIR/snapshot-empty.obj',
+ '$WEBKIT_DIR/JavaScriptCore_pcre.lib',
+ '$WEBKIT_DIR/Port.lib',
+ '$WEBKIT_DIR/activex_shim/activex_shim.lib',
+ '$WEBKIT_DIR/build/JavaScriptCore/WTF.lib',
+ '$WEBKIT_DIR/build/V8Bindings/V8Bindings.lib',
+ '$WEBKIT_DIR/build/WebCore/WebCore.lib',
+ '$WEBKIT_DIR/default_plugin/default_plugin.lib',
+ '$WEBKIT_DIR/glue/Glue.lib',
+]
+
+test_shell = env.Program(['test_shell.exe',
+ 'test_shell.ilk',
+ 'test_shell.pdb'],
+ components + resources +
+ ['test_shell_main.cc'])
+i = env.Install('$TARGET_ROOT', test_shell)
+env.Alias('webkit', i)
+
+# This call can NOT use $WEBKIT_DIR because we need to copy
+# directly from the source location.
+i = env.Install('$TARGET_ROOT', '#/../webkit/tools/test_shell/resources/fonts')
+env.Alias('webkit', i)
+
+env.Depends(test_shell, '$V8_DIR/vc80.pdb')
+
+
+test_files = [
+ 'drag_delegate.cc',
+ 'drop_delegate.cc',
+ 'event_sending_controller.cc',
+ 'image_decoder_unittest.cc',
+ 'keyboard_unittest.cc',
+ 'layout_test_controller.cc',
+ 'layout_test_controller_unittest.cc',
+ 'node_leak_test.cc',
+ 'plugin_tests.cc',
+ 'run_all_tests.cc',
+ 'simple_resource_loader_bridge.cc',
+ 'temp/navigation_controller_base.cc',
+ 'test_navigation_controller.cc',
+ 'test_shell.cc',
+ 'test_shell_request_context.cc',
+ 'test_shell_switches.cc',
+ 'test_shell_test.cc',
+ 'test_webview_delegate.cc',
+ 'text_input_controller.cc',
+ 'text_input_controller_unittest.cc',
+ 'webview_host.cc',
+ 'webwidget_host.cc',
+ '$WEBKIT_DIR/glue/autocomplete_input_listener_unittest.cc',
+ '$WEBKIT_DIR/glue/bookmarklet_unittest.cc',
+ '$WEBKIT_DIR/glue/context_menu_unittest.cc',
+ '$WEBKIT_DIR/glue/cpp_bound_class_unittest.cc',
+ '$WEBKIT_DIR/glue/cpp_variant_unittest.cc',
+ '$WEBKIT_DIR/glue/dom_operations_unittest.cc',
+ '$WEBKIT_DIR/glue/dom_serializer_unittest.cc',
+ '$WEBKIT_DIR/glue/glue_serialize_unittest.cc',
+ '$WEBKIT_DIR/glue/iframe_redirect_unittest.cc',
+ '$WEBKIT_DIR/glue/mimetype_unittest.cc',
+ '$WEBKIT_DIR/glue/multipart_response_delegate_unittest.cc',
+ '$WEBKIT_DIR/glue/password_autocomplete_listener_unittest.cc',
+ '$WEBKIT_DIR/glue/regular_expression_unittest.cc',
+ '$WEBKIT_DIR/glue/resource_fetcher_unittest.cc',
+ # Commented out until a regression is fixed and this file is restored.
+ #'$WEBKIT_DIR/glue/stringimpl_unittest.cc',
+ '$WEBKIT_DIR/glue/webplugin_impl_unittest.cc',
+ '$WEBKIT_DIR/port/platform/GKURL_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/bmp/BMPImageDecoder_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/ico/ICOImageDecoder_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/xbm/XBMImageDecoder_unittest.cpp',
+]
+
+test_shell_tests = env.Program(['test_shell_tests',
+ 'test_shell_tests.ilk',
+ 'test_shell_tests.pdb'],
+ components + resources + test_files)
+i = env.Install('$TARGET_ROOT', test_shell_tests)
+env.Alias('webkit', i)
diff --git a/webkit/tools/test_shell/drag_delegate.cc b/webkit/tools/test_shell/drag_delegate.cc
new file mode 100644
index 0000000..f0d01a8
--- /dev/null
+++ b/webkit/tools/test_shell/drag_delegate.cc
@@ -0,0 +1,65 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/drag_delegate.h"
+
+#include <atltypes.h>
+
+#include "webkit/glue/webview.h"
+
+namespace {
+
+void GetCursorPositions(HWND hwnd, CPoint* client, CPoint* screen) {
+ // GetCursorPos will fail if the input desktop isn't the current desktop.
+ // See http://b/1173534. (0,0) is wrong, but better than uninitialized.
+ if (!GetCursorPos(screen))
+ screen->SetPoint(0, 0);
+
+ *client = *screen;
+ ScreenToClient(hwnd, client);
+}
+
+} // anonymous namespace
+
+void TestDragDelegate::OnDragSourceCancel() {
+ OnDragSourceDrop();
+}
+
+void TestDragDelegate::OnDragSourceDrop() {
+ CPoint client;
+ CPoint screen;
+ GetCursorPositions(source_hwnd_, &client, &screen);
+ webview_->DragSourceEndedAt(client.x, client.y, screen.x, screen.y);
+}
+void TestDragDelegate::OnDragSourceMove() {
+ CPoint client;
+ CPoint screen;
+ GetCursorPositions(source_hwnd_, &client, &screen);
+ webview_->DragSourceMovedTo(client.x, client.y, screen.x, screen.y);
+}
diff --git a/webkit/tools/test_shell/drag_delegate.h b/webkit/tools/test_shell/drag_delegate.h
new file mode 100644
index 0000000..2872cdf
--- /dev/null
+++ b/webkit/tools/test_shell/drag_delegate.h
@@ -0,0 +1,60 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A class that implements BaseDragSource for the test shell webview delegate.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
+
+#include "base/base_drag_source.h"
+
+class WebView;
+
+class TestDragDelegate : public BaseDragSource {
+ public:
+ TestDragDelegate(HWND source_hwnd, WebView* webview)
+ : BaseDragSource(),
+ source_hwnd_(source_hwnd),
+ webview_(webview) { }
+
+ protected:
+ // BaseDragSource
+ virtual void OnDragSourceCancel();
+ virtual void OnDragSourceDrop();
+ virtual void OnDragSourceMove();
+
+ private:
+ WebView* webview_;
+
+ // A HWND for the source we are associated with, used for translating
+ // mouse coordinates from screen to client coordinates.
+ HWND source_hwnd_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
diff --git a/webkit/tools/test_shell/drop_delegate.cc b/webkit/tools/test_shell/drop_delegate.cc
new file mode 100644
index 0000000..0463190
--- /dev/null
+++ b/webkit/tools/test_shell/drop_delegate.cc
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/drop_delegate.h"
+
+#include "webkit/glue/webdropdata.h"
+#include "webkit/glue/webview.h"
+
+// BaseDropTarget methods ----------------------------------------------------
+DWORD TestDropDelegate::OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ WebDropData drop_data;
+ WebDropData::PopulateWebDropData(data_object, &drop_data);
+
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ bool valid = webview_->DragTargetDragEnter(drop_data, client_pt.x,
+ client_pt.y, cursor_position.x, cursor_position.y);
+ return valid ? DROPEFFECT_COPY : DROPEFFECT_NONE;
+}
+
+DWORD TestDropDelegate::OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ bool valid = webview_->DragTargetDragOver(client_pt.x,
+ client_pt.y, cursor_position.x, cursor_position.y);
+ return valid ? DROPEFFECT_COPY : DROPEFFECT_NONE;
+}
+
+void TestDropDelegate::OnDragLeave(IDataObject* data_object) {
+ webview_->DragTargetDragLeave();
+}
+
+DWORD TestDropDelegate::OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ webview_->DragTargetDrop(client_pt.x, client_pt.y,
+ cursor_position.x, cursor_position.y);
+
+ // webkit win port always returns DROPEFFECT_NONE
+ return DROPEFFECT_NONE;
+}
diff --git a/webkit/tools/test_shell/drop_delegate.h b/webkit/tools/test_shell/drop_delegate.h
new file mode 100644
index 0000000..e2fa493
--- /dev/null
+++ b/webkit/tools/test_shell/drop_delegate.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A class that implements BaseDropTarget for the test shell webview delegate.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
+
+#include "base/base_drop_target.h"
+
+class WebView;
+
+class TestDropDelegate : public BaseDropTarget {
+ public:
+ TestDropDelegate(HWND source_hwnd, WebView* webview)
+ : BaseDropTarget(source_hwnd),
+ webview_(webview) { }
+
+ protected:
+ // BaseDropTarget methods
+ virtual DWORD OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual DWORD OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual void OnDragLeave(IDataObject* data_object);
+ virtual DWORD OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+
+ private:
+ WebView* webview_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
diff --git a/webkit/tools/test_shell/event_sending_controller.cc b/webkit/tools/test_shell/event_sending_controller.cc
new file mode 100644
index 0000000..dee5eaf
--- /dev/null
+++ b/webkit/tools/test_shell/event_sending_controller.cc
@@ -0,0 +1,511 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains the definition for EventSendingController.
+//
+// Some notes about drag and drop handling:
+// Windows drag and drop goes through a system call to DoDragDrop. At that
+// point, program control is given to Windows which then periodically makes
+// callbacks into the webview. This won't work for layout tests, so instead,
+// we queue up all the mouse move and mouse up events. When the test tries to
+// start a drag (by calling EvenSendingController::DoDragDrop), we take the
+// events in the queue and replay them.
+// The behavior of queueing events and replaying them can be disabled by a
+// layout test by setting eventSender.dragMode to false.
+
+#include "webkit/tools/test_shell/event_sending_controller.h"
+
+#include <objidl.h>
+#include <queue>
+
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "webkit/glue/webview.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+// TODO(mpcomplete): layout before each event?
+// TODO(mpcomplete): do we need modifiers for mouse events?
+
+TestShell* EventSendingController::shell_ = NULL;
+gfx::Point EventSendingController::last_mouse_pos_;
+WebMouseEvent::Button EventSendingController::pressed_button_ =
+ WebMouseEvent::BUTTON_NONE;
+
+namespace {
+
+static scoped_refptr<IDataObject> drag_data_object;
+static bool replaying_saved_events = false;
+static std::queue<WebMouseEvent> mouse_event_queue;
+
+// Time and place of the last mouse up event.
+static double last_click_time_sec = 0;
+static gfx::Point last_click_pos;
+static int click_count = 0;
+
+// maximum distance (in space and time) for a mouse click
+// to register as a double or triple click
+static const double kMultiClickTimeSec = 1;
+static const int kMultiClickRadiusPixels = 5;
+
+inline bool outside_multiclick_radius(const gfx::Point &a, const gfx::Point &b) {
+ return ((a.x() - b.x()) * (a.x() - b.x()) + (a.y() - b.y()) * (a.y() - b.y())) >
+ kMultiClickRadiusPixels * kMultiClickRadiusPixels;
+}
+
+// Used to offset the time the event hander things an event happened. This is
+// done so tests can run without a delay, but bypass checks that are time
+// dependent (e.g., dragging has a timeout vs selection).
+static uint32 time_offset_ms = 0;
+
+double GetCurrentEventTimeSec() {
+ return (GetTickCount() + time_offset_ms) / 1000.0;
+}
+
+void AdvanceEventTime(int32 delta_ms) {
+ time_offset_ms += delta_ms;
+}
+
+void InitMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b,
+ const gfx::Point& pos, WebMouseEvent* e) {
+ e->type = t;
+ e->button = b;
+ e->modifiers = 0;
+ e->x = pos.x();
+ e->y = pos.y();
+ e->global_x = pos.x();
+ e->global_y = pos.y();
+ e->timestamp_sec = GetCurrentEventTimeSec();
+ e->layout_test_click_count = click_count;
+}
+
+void ApplyKeyModifiers(const CppVariant* arg, WebKeyboardEvent* event) {
+ std::vector<std::wstring> args = arg->ToStringVector();
+ for (std::vector<std::wstring>::iterator i = args.begin();
+ i != args.end(); ++i) {
+ const wchar_t* arg_string = (*i).c_str();
+ if (!wcscmp(arg_string, L"ctrlKey")) {
+ event->modifiers |= WebInputEvent::CTRL_KEY;
+ } else if (!wcscmp(arg_string, L"shiftKey")) {
+ event->modifiers |= WebInputEvent::SHIFT_KEY;
+ } else if (!wcscmp(arg_string, L"altKey")) {
+ event->modifiers |= WebInputEvent::ALT_KEY;
+ event->system_key = true;
+ } else if (!wcscmp(arg_string, L"metaKey")) {
+ event->modifiers |= WebInputEvent::META_KEY;
+ }
+ }
+}
+
+} // anonymous namespace
+
+EventSendingController::EventSendingController(TestShell* shell) {
+ // Set static shell_ variable since we can't do it in an initializer list.
+ // We also need to be careful not to assign shell_ to new windows which are
+ // temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling BindToJavaScript() (defined
+ // by CppBoundClass, the parent to EventSendingController).
+ BindMethod("mouseDown", &EventSendingController::mouseDown);
+ BindMethod("mouseUp", &EventSendingController::mouseUp);
+ BindMethod("contextClick", &EventSendingController::contextClick);
+ BindMethod("mouseMoveTo", &EventSendingController::mouseMoveTo);
+ BindMethod("leapForward", &EventSendingController::leapForward);
+ BindMethod("keyDown", &EventSendingController::keyDown);
+ BindMethod("enableDOMUIEventLogging", &EventSendingController::enableDOMUIEventLogging);
+ BindMethod("fireKeyboardEventsToElement", &EventSendingController::fireKeyboardEventsToElement);
+ BindMethod("clearKillRing", &EventSendingController::clearKillRing);
+ BindMethod("textZoomIn", &EventSendingController::textZoomIn);
+ BindMethod("textZoomOut", &EventSendingController::textZoomOut);
+
+ // When set to true (the default value), we batch mouse move and mouse up
+ // events so we can simulate drag & drop.
+ BindProperty("dragMode", &dragMode);
+}
+
+void EventSendingController::Reset() {
+ // The test should have finished a drag and the mouse button state.
+ DCHECK(!drag_data_object);
+ drag_data_object = NULL;
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+ dragMode.Set(true);
+ last_click_time_sec = 0;
+ click_count = 0;
+}
+
+/* static */ WebView* EventSendingController::webview() {
+ return shell_->webView();
+}
+
+/* static */ void EventSendingController::DoDragDrop(IDataObject* data_obj) {
+ drag_data_object = data_obj;
+
+ DWORD effect = 0;
+ POINTL screen_ptl = {0, 0};
+ TestWebViewDelegate* delegate = shell_->delegate();
+ delegate->drop_delegate()->DragEnter(drag_data_object, MK_LBUTTON,
+ screen_ptl, &effect);
+
+ // Finish processing events.
+ ReplaySavedEvents();
+}
+
+//
+// Implemented javascript methods.
+//
+
+ void EventSendingController::mouseDown(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ if ((GetCurrentEventTimeSec() - last_click_time_sec >= kMultiClickTimeSec) ||
+ outside_multiclick_radius(last_mouse_pos_, last_click_pos)) {
+ click_count = 1;
+ } else {
+ ++click_count;
+ }
+
+ WebMouseEvent event;
+ pressed_button_ = WebMouseEvent::BUTTON_LEFT;
+ InitMouseEvent(WebInputEvent::MOUSE_DOWN, WebMouseEvent::BUTTON_LEFT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+}
+
+ void EventSendingController::mouseUp(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ WebMouseEvent event;
+ InitMouseEvent(WebInputEvent::MOUSE_UP, WebMouseEvent::BUTTON_LEFT,
+ last_mouse_pos_, &event);
+ if (drag_mode() && !replaying_saved_events) {
+ mouse_event_queue.push(event);
+ ReplaySavedEvents();
+ } else {
+ DoMouseUp(event);
+ }
+
+ last_click_time_sec = event.timestamp_sec;
+ last_click_pos = gfx::Point(event.x, event.y);
+}
+
+/* static */ void EventSendingController::DoMouseUp(const WebMouseEvent& e) {
+ webview()->HandleInputEvent(&e);
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+
+ // If we're in a drag operation, complete it.
+ if (drag_data_object) {
+ TestWebViewDelegate* delegate = shell_->delegate();
+ // Get screen mouse position.
+ POINT screen_pt = { static_cast<LONG>(e.x),
+ static_cast<LONG>(e.y) };
+ ClientToScreen(shell_->webViewWnd(), &screen_pt);
+ POINTL screen_ptl = { screen_pt.x, screen_pt.y };
+
+ DWORD effect = 0;
+ delegate->drop_delegate()->DragOver(0, screen_ptl, &effect);
+ HRESULT hr = delegate->drag_delegate()->QueryContinueDrag(0, 0);
+ if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) {
+ DWORD effect = 0;
+ delegate->drop_delegate()->Drop(drag_data_object.get(), 0, screen_ptl,
+ &effect);
+ } else {
+ delegate->drop_delegate()->DragLeave();
+ }
+ drag_data_object = NULL;
+ }
+}
+
+ void EventSendingController::mouseMoveTo(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ webview()->Layout();
+
+ WebMouseEvent event;
+ last_mouse_pos_.SetPoint(args[0].ToInt32(), args[1].ToInt32());
+ InitMouseEvent(WebInputEvent::MOUSE_MOVE, pressed_button_,
+ last_mouse_pos_, &event);
+
+ if (drag_mode() && pressed_button_ != WebMouseEvent::BUTTON_NONE &&
+ !replaying_saved_events) {
+ mouse_event_queue.push(event);
+ } else {
+ DoMouseMove(event);
+ }
+ }
+}
+
+/* static */ void EventSendingController::DoMouseMove(const WebMouseEvent& e) {
+ webview()->HandleInputEvent(&e);
+
+ if (pressed_button_ != WebMouseEvent::BUTTON_NONE && drag_data_object) {
+ TestWebViewDelegate* delegate = shell_->delegate();
+ // Get screen mouse position.
+ POINT screen_pt = { static_cast<LONG>(e.x),
+ static_cast<LONG>(e.y) };
+ ClientToScreen(shell_->webViewWnd(), &screen_pt);
+ POINTL screen_ptl = { screen_pt.x, screen_pt.y };
+
+ HRESULT hr = delegate->drag_delegate()->QueryContinueDrag(0, MK_LBUTTON);
+ DWORD effect = 0;
+ delegate->drop_delegate()->DragOver(MK_LBUTTON, screen_ptl, &effect);
+
+ delegate->drag_delegate()->GiveFeedback(effect);
+ }
+}
+
+ void EventSendingController::keyDown(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ static const int kPercentVirtualKeyCode = 0x25;
+ static const int kAmpersandVirtualKeyCode = 0x26;
+
+ static const int kLeftParenthesesVirtualKeyCode = 0x28;
+ static const int kRightParenthesesVirtualKeyCode = 0x29;
+
+ static const int kLeftCurlyBracketVirtualKeyCode = 0x7B;
+ static const int kRightCurlyBracketVirtualKeyCode = 0x7D;
+
+ bool generate_char = false;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ // TODO(mpcomplete): I'm not exactly sure how we should convert the string
+ // to a key event. This seems to work in the cases I tested.
+ // TODO(mpcomplete): Should we also generate a KEY_UP?
+ std::wstring code_str = UTF8ToWide(args[0].ToString());
+
+ // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when
+ // Windows uses \r for "Enter".
+ wchar_t code;
+ bool needs_shift_key_modifier = false;
+ if (L"\n" == code_str) {
+ generate_char = true;
+ code = VK_RETURN;
+ } else if (L"rightArrow" == code_str) {
+ code = VK_RIGHT;
+ } else if (L"downArrow" == code_str) {
+ code = VK_DOWN;
+ } else if (L"leftArrow" == code_str) {
+ code = VK_LEFT;
+ } else if (L"upArrow" == code_str) {
+ code = VK_UP;
+ } else if (L"delete" == code_str) {
+ code = VK_BACK;
+ } else {
+ DCHECK(code_str.length() == 1);
+ code = code_str[0];
+ needs_shift_key_modifier = NeedsShiftModifer(code);
+ generate_char = true;
+ }
+
+ // NOTE(jnd):For one keydown event, we need to generate
+ // keyDown/keyUp pair, refer EventSender.cpp in
+ // WebKit/WebKitTools/DumpRenderTree/win. We may also need
+ // to generate a keyChar event in certain cases.
+ WebKeyboardEvent event_down, event_up;
+ event_down.type = WebInputEvent::KEY_DOWN;
+ event_down.modifiers = 0;
+ event_down.key_code = code;
+ event_down.key_data = code;
+
+ if (args.size() >= 2 && args[1].isObject())
+ ApplyKeyModifiers(&(args[1]), &event_down);
+
+ if (needs_shift_key_modifier)
+ event_down.modifiers |= WebInputEvent::SHIFT_KEY;
+
+ event_up = event_down;
+ event_up.type = WebInputEvent::KEY_UP;
+ // EventSendingController.m forces a layout here, with at least one
+ // test (fast\forms\focus-control-to-page.html) relying on this.
+ webview()->Layout();
+
+ webview()->HandleInputEvent(&event_down);
+
+ if (generate_char) {
+ WebKeyboardEvent event_char = event_down;
+ if (event_down.modifiers & WebInputEvent::SHIFT_KEY) {
+ // Special case for the following characters when the shift key is
+ // pressed in conjunction with these characters.
+ // Windows generates a WM_KEYDOWN message with the ASCII code of
+ // the character followed by a WM_CHAR for the corresponding
+ // virtual key code.
+ // We check for these keys to catch regressions in keyEvent handling
+ // in webkit.
+ switch(code) {
+ case '5':
+ event_char.key_code = kPercentVirtualKeyCode;
+ event_char.key_data = kPercentVirtualKeyCode;
+ break;
+ case '7':
+ event_char.key_code = kAmpersandVirtualKeyCode;
+ event_char.key_data = kAmpersandVirtualKeyCode;
+ break;
+ case '9':
+ event_char.key_code = kLeftParenthesesVirtualKeyCode;
+ event_char.key_data = kLeftParenthesesVirtualKeyCode;
+ break;
+ case '0':
+ event_char.key_code = kRightParenthesesVirtualKeyCode;
+ event_char.key_data = kRightParenthesesVirtualKeyCode;
+ break;
+ // '[{' for US
+ case VK_OEM_4:
+ event_char.key_code = kLeftCurlyBracketVirtualKeyCode;
+ event_char.key_data = kLeftCurlyBracketVirtualKeyCode;
+ break;
+ // ']}' for US
+ case VK_OEM_6:
+ event_char.key_code = kRightCurlyBracketVirtualKeyCode;
+ event_char.key_data = kRightCurlyBracketVirtualKeyCode;
+ break;
+ default:
+ break;
+ }
+ }
+ event_char.type = WebInputEvent::CHAR;
+ webview()->HandleInputEvent(&event_char);
+ }
+
+ webview()->HandleInputEvent(&event_up);
+ }
+}
+
+ bool EventSendingController::NeedsShiftModifer(wchar_t key_code) {
+ // If code is an uppercase letter, assign a SHIFT key to
+ // event_down.modifier, this logic comes from
+ // WebKit/WebKitTools/DumpRenderTree/Win/EventSender.cpp
+ if ((LOBYTE(key_code)) >= 'A' && (LOBYTE(key_code)) <= 'Z')
+ return true;
+ return false;
+ }
+
+ void EventSendingController::leapForward(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ // TODO(mpcomplete): DumpRenderTree defers this under certain conditions.
+
+ if (args.size() >=1 && args[0].isNumber()) {
+ AdvanceEventTime(args[0].ToInt32());
+ }
+}
+
+// Apple's port of webkit zooms by a factor of 1.2 (see
+// WebKit/WebView/WebView.mm)
+ void EventSendingController::textZoomIn(
+ const CppArgumentList& args, CppVariant* result) {
+ webview()->MakeTextLarger();
+ result->SetNull();
+}
+
+ void EventSendingController::textZoomOut(
+ const CppArgumentList& args, CppVariant* result) {
+ webview()->MakeTextSmaller();
+ result->SetNull();
+}
+
+ void EventSendingController::ReplaySavedEvents() {
+ replaying_saved_events = true;
+ while (!mouse_event_queue.empty()) {
+ WebMouseEvent event = mouse_event_queue.front();
+ mouse_event_queue.pop();
+
+ switch (event.type) {
+ case WebInputEvent::MOUSE_UP:
+ DoMouseUp(event);
+ break;
+ case WebInputEvent::MOUSE_MOVE:
+ DoMouseMove(event);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ replaying_saved_events = false;
+}
+
+ void EventSendingController::contextClick(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ if (GetCurrentEventTimeSec() - last_click_time_sec >= 1) {
+ click_count = 1;
+ } else {
+ ++click_count;
+ }
+
+ // Generate right mouse down and up.
+
+ WebMouseEvent event;
+ pressed_button_ = WebMouseEvent::BUTTON_RIGHT;
+ InitMouseEvent(WebInputEvent::MOUSE_DOWN, WebMouseEvent::BUTTON_RIGHT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+ InitMouseEvent(WebInputEvent::MOUSE_UP, WebMouseEvent::BUTTON_RIGHT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+}
+
+//
+// Unimplemented stubs
+//
+
+ void EventSendingController::enableDOMUIEventLogging(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+ void EventSendingController::fireKeyboardEventsToElement(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+ void EventSendingController::clearKillRing(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
diff --git a/webkit/tools/test_shell/event_sending_controller.h b/webkit/tools/test_shell/event_sending_controller.h
new file mode 100644
index 0000000..ddcff48
--- /dev/null
+++ b/webkit/tools/test_shell/event_sending_controller.h
@@ -0,0 +1,108 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+/*
+ EventSendingController class:
+ Bound to a JavaScript window.eventSender object using
+ CppBoundClass::BindToJavascript(), this allows layout tests that are run in
+ the test_shell to fire DOM events.
+
+ The OSX reference file is in
+ WebKit/WebKitTools/DumpRenderTree/EventSendingController.m
+*/
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
+
+#include "base/gfx/point.h"
+#include "webkit/glue/cpp_bound_class.h"
+#include "webkit/glue/webinputevent.h"
+
+struct IDataObject;
+struct IDropSource;
+class TestShell;
+class WebView;
+
+class EventSendingController : public CppBoundClass {
+ public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ EventSendingController(TestShell* shell);
+
+ // Resets some static variable state.
+ void Reset();
+
+ // Simulate Windows' drag&drop system call.
+ static void DoDragDrop(IDataObject* drag_data);
+
+ // JS callback methods.
+ void mouseDown(const CppArgumentList& args, CppVariant* result);
+ void mouseUp(const CppArgumentList& args, CppVariant* result);
+ void mouseMoveTo(const CppArgumentList& args, CppVariant* result);
+ void leapForward(const CppArgumentList& args, CppVariant* result);
+ void keyDown(const CppArgumentList& args, CppVariant* result);
+ void textZoomIn(const CppArgumentList& args, CppVariant* result);
+ void textZoomOut(const CppArgumentList& args, CppVariant* result);
+
+ // Unimplemented stubs
+ void contextClick(const CppArgumentList& args, CppVariant* result);
+ void enableDOMUIEventLogging(const CppArgumentList& args, CppVariant* result);
+ void fireKeyboardEventsToElement(const CppArgumentList& args, CppVariant* result);
+ void clearKillRing(const CppArgumentList& args, CppVariant* result);
+ CppVariant dragMode;
+
+ private:
+ // Returns the test shell's webview.
+ static WebView* webview();
+
+ // Returns true if dragMode is true.
+ bool drag_mode() { return dragMode.isBool() && dragMode.ToBoolean(); }
+
+ // Sometimes we queue up mouse move and mouse up events for drag drop
+ // handling purposes. These methods dispatch the event.
+ static void DoMouseMove(const WebMouseEvent& e);
+ static void DoMouseUp(const WebMouseEvent& e);
+ static void ReplaySavedEvents();
+
+ // Returns true if the key_code passed in needs a shift key modifier to
+ // be passed into the generated event.
+ bool NeedsShiftModifer(wchar_t key_code);
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+
+ // Location of last mouseMoveTo event.
+ static gfx::Point last_mouse_pos_;
+
+ // Currently pressed mouse button (Left/Right/Middle or None)
+ static WebMouseEvent::Button pressed_button_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/foreground_helper.h b/webkit/tools/test_shell/foreground_helper.h
new file mode 100644
index 0000000..359428e
--- /dev/null
+++ b/webkit/tools/test_shell/foreground_helper.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <atlbase.h>
+#include <atlwin.h>
+
+#include "base/logging.h"
+
+// Helper class for moving a window to the foreground.
+// Windows XP and later will not allow a window which is in the background to
+// move to the foreground, unless requested by the current window in the
+// foreground. For automated testing, we really want some of our windows
+// to be capable of moving to the foreground.
+//
+// This is probably leveraging a windows bug.
+class ForegroundHelper : public CWindowImpl<ForegroundHelper> {
+ public:
+BEGIN_MSG_MAP(ForegroundHelper)
+ MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
+END_MSG_MAP()
+
+ // Brings a window into the foreground.
+ // Can be called from any window, even if the caller is not the
+ // foreground window.
+ static HRESULT SetForeground(HWND window) {
+ DCHECK(::IsWindow(window));
+ ForegroundHelper foreground_helper;
+ CHECK(foreground_helper.ForegroundHotKey(window) == S_OK);
+ return S_OK;
+ }
+
+ private:
+ HRESULT ForegroundHotKey(HWND window) {
+ // This implementation registers a hot key (F22) and then
+ // triggers the hot key. When receiving the hot key, we'll
+ // be in the foreground and allowed to move the target window
+ // into the foreground too.
+
+ if(NULL == Create(NULL, NULL, NULL, WS_POPUP))
+ return AtlHresultFromLastError();
+
+ static const int hotkey_id = 0x0000baba;
+
+ // Store the target window into our USERDATA for use in our
+ // HotKey handler.
+ SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window));
+ RegisterHotKey(m_hWnd, hotkey_id, 0, VK_F22);
+
+ // If the calling thread is not yet a UI thread, call PeekMessage
+ // to ensure creation of its message queue.
+ MSG msg = {0};
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+
+ // Send the Hotkey.
+ INPUT hotkey = {0};
+ hotkey.type = INPUT_KEYBOARD;
+ hotkey.ki.wVk = VK_F22;
+ if (1 != SendInput(1, &hotkey, sizeof(hotkey)))
+ return E_FAIL;
+
+ // Loop until we get the key.
+ // TODO: It may be possible to get stuck here if the
+ // message gets lost?
+ while(GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ if(WM_HOTKEY == msg.message)
+ break;
+ }
+
+ UnregisterHotKey(m_hWnd, hotkey_id);
+ DestroyWindow();
+
+ return S_OK;
+ }
+
+ // Handle the registered Hotkey being pressed.
+ LRESULT OnHotKey(UINT /*uMsg*/, WPARAM /*wParam*/,
+ LPARAM /*lParam*/, BOOL& bHandled) {
+ HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA));
+ SetForegroundWindow(window);
+ return 1;
+ }
+};
diff --git a/webkit/tools/test_shell/image_decoder_unittest.cc b/webkit/tools/test_shell/image_decoder_unittest.cc
new file mode 100644
index 0000000..9e640fd
--- /dev/null
+++ b/webkit/tools/test_shell/image_decoder_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/md5.h"
+#include "base/path_service.h"
+#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "webkit/tools/test_shell/image_decoder_unittest.h"
+
+void ReadFileToVector(const std::wstring& path, Vector<char>* contents) {
+ std::string contents_str;
+ file_util::ReadFileToString(path, &contents_str);
+ contents->resize(contents_str.size());
+ memcpy(&contents->first(), contents_str.data(), contents_str.size());
+}
+
+std::wstring GetMD5SumPath(const std::wstring& path) {
+ static const std::wstring kDecodedDataExtension(L".md5sum");
+ return path + kDecodedDataExtension;
+}
+
+#ifdef CALCULATE_MD5_SUMS
+void SaveMD5Sum(const std::wstring& path, WebCore::RGBA32Buffer* buffer) {
+ // Create the file to write.
+ ScopedHandle handle(CreateFile(path.c_str(), GENERIC_WRITE, 0,
+ NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL));
+ ASSERT_TRUE(handle.IsValid());
+
+ // Calculate MD5 sum.
+ MD5Digest digest;
+ SkAutoLockPixels bmp_lock(buffer->bitmap());
+ MD5Sum(buffer->bitmap().getPixels(),
+ buffer->rect().width() * buffer->rect().height() * sizeof(unsigned),
+ &digest);
+
+ // Write sum to disk.
+ DWORD bytes_written;
+ ASSERT_TRUE(WriteFile(handle, &digest, sizeof digest, &bytes_written,
+ NULL));
+ ASSERT_EQ(sizeof digest, bytes_written);
+}
+#else
+void VerifyImage(WebCore::ImageDecoder* decoder,
+ const std::wstring& path,
+ const std::wstring& md5_sum_path) {
+ // Make sure decoding can complete successfully.
+ EXPECT_TRUE(decoder->isSizeAvailable()) << path;
+ WebCore::RGBA32Buffer* image_buffer = decoder->frameBufferAtIndex(0);
+ ASSERT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL), image_buffer) << path;
+ EXPECT_EQ(WebCore::RGBA32Buffer::FrameComplete, image_buffer->status()) <<
+ path;
+ EXPECT_FALSE(decoder->failed()) << path;
+
+ // Calculate MD5 sum.
+ MD5Digest actual_digest;
+ SkAutoLockPixels bmp_lock(image_buffer->bitmap());
+ MD5Sum(image_buffer->bitmap().getPixels(), image_buffer->rect().width() *
+ image_buffer->rect().height() * sizeof(unsigned), &actual_digest);
+
+ // Read the MD5 sum off disk.
+ std::string file_bytes;
+ file_util::ReadFileToString(md5_sum_path, &file_bytes);
+ MD5Digest expected_digest;
+ ASSERT_EQ(sizeof expected_digest, file_bytes.size()) << path;
+ memcpy(&expected_digest, file_bytes.data(), sizeof expected_digest);
+
+ // Verify that the sums are the same.
+ EXPECT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof(MD5Digest))) <<
+ path;
+}
+#endif
+
+void ImageDecoderTest::SetUp() {
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_));
+ file_util::AppendToPath(&data_dir_, L"webkit");
+ file_util::AppendToPath(&data_dir_, L"data");
+ file_util::AppendToPath(&data_dir_, format_ + L"_decoder");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+}
+
+std::vector<std::wstring> ImageDecoderTest::GetImageFiles() const {
+ std::wstring find_string(data_dir_);
+ file_util::AppendToPath(&find_string, L"*." + format_);
+ WIN32_FIND_DATA find_data;
+ ScopedFindFileHandle handle(FindFirstFile(find_string.c_str(),
+ &find_data));
+ EXPECT_TRUE(handle.IsValid());
+
+ std::vector<std::wstring> image_files;
+ do {
+ std::wstring image_path = data_dir_;
+ file_util::AppendToPath(&image_path, find_data.cFileName);
+ image_files.push_back(image_path);
+ } while (FindNextFile(handle, &find_data));
+
+ return image_files;
+}
+
+bool ImageDecoderTest::ShouldImageFail(const std::wstring& path) const {
+ static const std::wstring kBadSuffix(L".bad.");
+ return (path.length() > (kBadSuffix.length() + format_.length()) &&
+ !path.compare(path.length() - format_.length() - kBadSuffix.length(),
+ kBadSuffix.length(), kBadSuffix));
+}
+
+void ImageDecoderTest::TestDecoding() const {
+ const std::vector<std::wstring> image_files(GetImageFiles());
+ for (std::vector<std::wstring>::const_iterator i(image_files.begin());
+ i != image_files.end(); ++i) {
+ Vector<char> image_contents;
+ ReadFileToVector(*i, &image_contents);
+
+ scoped_ptr<WebCore::ImageDecoder> decoder(CreateDecoder());
+ RefPtr<WebCore::SharedBuffer> shared_contents(new WebCore::SharedBuffer);
+ shared_contents->append(image_contents.data(),
+ static_cast<int>(image_contents.size()));
+ decoder->setData(shared_contents.get(), true);
+
+ if (ShouldImageFail(*i)) {
+ // We should always get a non-NULL frame buffer, but when the decoder
+ // tries to produce it, it should fail, and the frame buffer shouldn't
+ // complete.
+ WebCore::RGBA32Buffer* const image_buffer =
+ decoder->frameBufferAtIndex(0);
+ ASSERT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL), image_buffer) <<
+ (*i);
+ EXPECT_NE(image_buffer->status(), WebCore::RGBA32Buffer::FrameComplete) <<
+ (*i);
+ EXPECT_TRUE(decoder->failed()) << (*i);
+ continue;
+ }
+
+#ifdef CALCULATE_MD5_SUMS
+ SaveMD5Sum(GetMD5SumPath(*i), decoder->frameBufferAtIndex(0));
+#else
+ VerifyImage(decoder.get(), *i, GetMD5SumPath(*i));
+#endif
+ }
+}
+
+#ifndef CALCULATE_MD5_SUMS
+void ImageDecoderTest::TestChunkedDecoding() const {
+ // Init random number generator with current day, so a failing case will fail
+ // consistently over the course of a whole day.
+ const Time today = Time::Now().LocalMidnight();
+ srand(static_cast<unsigned int>(today.ToInternalValue()));
+
+ const std::vector<std::wstring> image_files(GetImageFiles());
+ for (std::vector<std::wstring>::const_iterator i(image_files.begin());
+ i != image_files.end(); ++i) {
+ if (ShouldImageFail(*i))
+ continue;
+
+ // Read the file and split it at an arbitrary point.
+ Vector<char> image_contents;
+ ReadFileToVector(*i, &image_contents);
+ const int partial_size = static_cast<int>(
+ (static_cast<double>(rand()) / RAND_MAX) * image_contents.size());
+ RefPtr<WebCore::SharedBuffer> partial_contents(new WebCore::SharedBuffer);
+ partial_contents->append(image_contents.data(), partial_size);
+
+ // Make sure the image decoder doesn't fail when we ask for the frame buffer
+ // for this partial image.
+ scoped_ptr<WebCore::ImageDecoder> decoder(CreateDecoder());
+ decoder->setData(partial_contents.get(), false);
+ EXPECT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL),
+ decoder->frameBufferAtIndex(0)) << (*i);
+ EXPECT_FALSE(decoder->failed()) << (*i);
+
+ // Make sure passing the complete image results in successful decoding.
+ partial_contents->append(
+ &image_contents.data()[partial_size],
+ static_cast<int>(image_contents.size() - partial_size));
+ decoder->setData(partial_contents.get(), true);
+ VerifyImage(decoder.get(), *i, GetMD5SumPath(*i));
+ }
+}
+#endif
diff --git a/webkit/tools/test_shell/image_decoder_unittest.h b/webkit/tools/test_shell/image_decoder_unittest.h
new file mode 100644
index 0000000..21a0f71
--- /dev/null
+++ b/webkit/tools/test_shell/image_decoder_unittest.h
@@ -0,0 +1,103 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "Vector.h"
+#include "ImageDecoder.h"
+
+// If CALCULATE_MD5_SUMS is not defined, then this test decodes a handful of
+// image files and compares their MD5 sums to the stored sums on disk.
+//
+// To recalculate the MD5 sums, uncommment CALCULATE_MD5_SUMS.
+//
+// The image files and corresponding MD5 sums live in the directory
+// chrome/test/data/*_decoder (where "*" is the format being tested).
+//
+// Note: The MD5 sums calculated in this test by little- and big-endian systems
+// will differ, since no endianness correction is done. If we start compiling
+// for big endian machines this should be fixed.
+
+//#define CALCULATE_MD5_SUMS
+
+// Reads the contents of the specified file into the specified vector.
+void ReadFileToVector(const std::wstring& path, Vector<char>* contents);
+
+// Returns the path the decoded data is saved at.
+std::wstring GetMD5SumPath(const std::wstring& path);
+
+#ifdef CALCULATE_MD5_SUMS
+// Saves the MD5 sum to the specified file.
+void SaveMD5Sum(const std::wstring& path, WebCore::RGBA32Buffer* buffer);
+#else
+// Verifies the image. |path| identifies the path the image was loaded from.
+void VerifyImage(WebCore::ImageDecoder* decoder,
+ const std::wstring& path,
+ const std::wstring& md5_sum_path);
+#endif
+
+class ImageDecoderTest : public testing::Test {
+ public:
+ explicit ImageDecoderTest(const std::wstring& format) : format_(format) { }
+
+ protected:
+ virtual void SetUp();
+
+ // Returns the vector of image files for testing.
+ std::vector<std::wstring> GetImageFiles() const;
+
+ // Returns true if the image is bogus and should not be successfully decoded.
+ bool ShouldImageFail(const std::wstring& path) const;
+
+ // Verifies each of the test image files is decoded correctly and matches the
+ // expected state.
+ void TestDecoding() const;
+
+#ifndef CALCULATE_MD5_SUMS
+ // Verifies that decoding still works correctly when the files are split into
+ // pieces at a random point.
+ void TestChunkedDecoding() const;
+#endif
+
+ // Returns the correct type of image decoder for this test.
+ virtual WebCore::ImageDecoder* CreateDecoder() const = 0;
+
+ // The format to be decoded, like "bmp" or "ico".
+ std::wstring format_;
+
+ protected:
+ // Path to the test files.
+ std::wstring data_dir_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ImageDecoderTest);
+};
diff --git a/webkit/tools/test_shell/keyboard_unittest.cc b/webkit/tools/test_shell/keyboard_unittest.cc
new file mode 100644
index 0000000..5f19e27
--- /dev/null
+++ b/webkit/tools/test_shell/keyboard_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+
+#pragma warning(push, 0)
+#include "EventNames.h"
+#include "EventTarget.h"
+#include "KeyboardEvent.h"
+#pragma warning(pop)
+
+#include "webkit/glue/editor_client_impl.h"
+#include "webkit/glue/event_conversion.h"
+#include "webkit/glue/webinputevent.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlReturn) {
+ WebCore::EventNames::init();
+
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 0xD;
+ keyboard_event.key_data = 0xD;
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlZ) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'Z';
+ keyboard_event.key_data = 'Z';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Undo");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlA) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'A';
+ keyboard_event.key_data = 'A';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "SelectAll");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlX) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'X';
+ keyboard_event.key_data = 'X';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+
+ EXPECT_STREQ(result, "Cut");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlC) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'C';
+ keyboard_event.key_data = 'C';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Copy");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlV) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'V';
+ keyboard_event.key_data = 'V';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Paste");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestEscape) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = VK_ESCAPE;
+ keyboard_event.key_data = VK_ESCAPE;
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Cancel");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestRedo) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'Y';
+ keyboard_event.key_data = 'Y';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Redo");
+}
+
+
+TEST(KeyboardUnitTestKeyPress, TestInsertTab) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\t';
+ keyboard_event.key_data = '\t';
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertTab");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertBackTab) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\t';
+ keyboard_event.key_data = '\t';
+ keyboard_event.modifiers = WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertBacktab");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline2) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertlinebreak) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertLineBreak");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline3) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::ALT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline4) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::ALT_KEY | WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+} \ No newline at end of file
diff --git a/webkit/tools/test_shell/layout_test_controller.cc b/webkit/tools/test_shell/layout_test_controller.cc
new file mode 100644
index 0000000..ff48763
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller.cc
@@ -0,0 +1,595 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains the definition for LayoutTestController.
+
+#include <vector>
+
+#include "webkit/tools/test_shell/layout_test_controller.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/path_service.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/webview.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+using std::string;
+using std::wstring;
+
+namespace {
+
+// Stops the test from running and prints a brief warning to stdout. Called
+// when the timer for loading a layout test expires.
+VOID CALLBACK TestTimeout(HWND hwnd, UINT msg, UINT_PTR timer_id, DWORD ms) {
+ reinterpret_cast<TestShell*>(timer_id)->TestFinished();
+ // Print a warning to be caught by the layout-test script.
+ puts("#TEST_TIMED_OUT\n");
+}
+
+}
+
+
+TestShell* LayoutTestController::shell_ = NULL;
+bool LayoutTestController::dump_as_text_ = false;
+bool LayoutTestController::dump_editing_callbacks_ = false;
+bool LayoutTestController::dump_frame_load_callbacks_ = false;
+bool LayoutTestController::dump_resource_load_callbacks_ = false;
+bool LayoutTestController::dump_back_forward_list_ = false;
+bool LayoutTestController::dump_child_frame_scroll_positions_ = false;
+bool LayoutTestController::dump_child_frames_as_text_ = false;
+bool LayoutTestController::dump_title_changes_ = false;
+bool LayoutTestController::accepts_editing_ = true;
+bool LayoutTestController::wait_until_done_ = false;
+bool LayoutTestController::can_open_windows_ = false;
+bool LayoutTestController::close_remaining_windows_ = true;
+bool LayoutTestController::should_add_file_to_pasteboard_ = false;
+LayoutTestController::WorkQueue LayoutTestController::work_queue_;
+CppVariant LayoutTestController::globalFlag_;
+
+LayoutTestController::LayoutTestController(TestShell* shell) {
+ // Set static shell_ variable since we can't do it in an initializer list.
+ // We also need to be careful not to assign shell_ to new windows which are
+ // temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling BindToJavaScript() (defined
+ // by CppBoundClass, the parent to LayoutTestController).
+ BindMethod("dumpAsText", &LayoutTestController::dumpAsText);
+ BindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions);
+ BindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText);
+ BindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks);
+ BindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList);
+ BindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks);
+ BindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks);
+ BindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges);
+ BindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing);
+ BindMethod("waitUntilDone", &LayoutTestController::waitUntilDone);
+ BindMethod("notifyDone", &LayoutTestController::notifyDone);
+ BindMethod("queueReload", &LayoutTestController::queueReload);
+ BindMethod("queueScript", &LayoutTestController::queueScript);
+ BindMethod("queueLoad", &LayoutTestController::queueLoad);
+ BindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation);
+ BindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation);
+ BindMethod("windowCount", &LayoutTestController::windowCount);
+ BindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows);
+ BindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete);
+ BindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual);
+ BindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey);
+ BindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements);
+ BindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation);
+ BindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled);
+ BindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource);
+ BindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag);
+ BindMethod("execCommand", &LayoutTestController::execCommand);
+
+ // The following are stubs.
+ BindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive);
+ BindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder);
+ BindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect);
+ BindMethod("display", &LayoutTestController::display);
+ BindMethod("testRepaint", &LayoutTestController::testRepaint);
+ BindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally);
+ BindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList);
+ BindMethod("keepWebHistory", &LayoutTestController::keepWebHistory);
+ BindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject);
+ BindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject);
+ BindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf);
+ BindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL);
+ BindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews);
+ BindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled);
+ BindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode);
+ BindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate);
+
+ // This typo (missing 'i') is intentional as it matches the typo in the layout test
+ // see: LayoutTests/fast/canvas/fill-stroke-clip-reset-path.html.
+ // If Apple ever fixes this, we'll need to update it.
+ BindMethod("setUseDashboardCompatiblityMode", &LayoutTestController::setUseDashboardCompatibilityMode);
+
+ // The fallback method is called when an unknown method is invoked.
+ BindFallbackMethod(&LayoutTestController::fallbackMethod);
+
+ // Shared property used by a number of layout tests in
+ // LayoutTests\http\tests\security\dataURL.
+ BindProperty("globalFlag", &globalFlag_);
+}
+
+LayoutTestController::WorkQueue::~WorkQueue() {
+ Reset();
+}
+
+void LayoutTestController::WorkQueue::ProcessWork() {
+ // Quit doing work once a load is in progress.
+ while (!queue_.empty() && !shell_->delegate()->top_loading_frame()) {
+ queue_.front()->Run(shell_);
+ delete queue_.front();
+ queue_.pop();
+ }
+
+ if (!shell_->delegate()->top_loading_frame() && !wait_until_done_) {
+ shell_->TestFinished();
+ }
+}
+
+void LayoutTestController::WorkQueue::Reset() {
+ frozen_ = false;
+ while (!queue_.empty()) {
+ delete queue_.front();
+ queue_.pop();
+ }
+}
+
+void LayoutTestController::WorkQueue::AddWork(WorkItem* work) {
+ if (frozen_) {
+ delete work;
+ return;
+ }
+ queue_.push(work);
+}
+
+void LayoutTestController::dumpAsText(const CppArgumentList& args,
+ CppVariant* result) {
+ dump_as_text_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpEditingCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_editing_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpBackForwardList(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_back_forward_list_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpFrameLoadCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_frame_load_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpResourceLoadCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_resource_load_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpChildFrameScrollPositions(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_child_frame_scroll_positions_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpChildFramesAsText(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_child_frames_as_text_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpTitleChanges(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_title_changes_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::setAcceptsEditing(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool())
+ accepts_editing_ = args[0].value.boolValue;
+ result->SetNull();
+}
+
+void LayoutTestController::waitUntilDone(
+ const CppArgumentList& args, CppVariant* result) {
+ // Set a timer in case something hangs. We use a custom timer rather than
+ // the one managed by the message loop so we can kill it when the load
+ // finishes successfully.
+ if (!::IsDebuggerPresent()) {
+ UINT_PTR timer_id = reinterpret_cast<UINT_PTR>(shell_);
+ SetTimer(shell_->mainWnd(), timer_id, shell_->GetFileTestTimeout(),
+ &TestTimeout);
+ }
+ wait_until_done_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::notifyDone(
+ const CppArgumentList& args, CppVariant* result) {
+ if (!shell_->interactive() && wait_until_done_ &&
+ !shell_->delegate()->top_loading_frame() && work_queue_.empty()) {
+ shell_->TestFinished();
+ }
+ wait_until_done_ = false;
+ result->SetNull();
+}
+
+class WorkItemBackForward : public LayoutTestController::WorkItem {
+ public:
+ WorkItemBackForward(int distance) : distance_(distance) {}
+ void Run(TestShell* shell) {
+ shell->GoBackOrForward(distance_);
+ }
+ private:
+ int distance_;
+};
+
+void LayoutTestController::queueBackNavigation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isNumber())
+ work_queue_.AddWork(new WorkItemBackForward(-args[0].ToInt32()));
+ result->SetNull();
+}
+
+void LayoutTestController::queueForwardNavigation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isNumber())
+ work_queue_.AddWork(new WorkItemBackForward(args[0].ToInt32()));
+ result->SetNull();
+}
+
+class WorkItemReload : public LayoutTestController::WorkItem {
+ public:
+ void Run(TestShell* shell) {
+ shell->Reload();
+ }
+};
+
+void LayoutTestController::queueReload(
+ const CppArgumentList& args, CppVariant* result) {
+ work_queue_.AddWork(new WorkItemReload);
+ result->SetNull();
+}
+
+class WorkItemScript : public LayoutTestController::WorkItem {
+ public:
+ WorkItemScript(const string& script) : script_(script) {}
+ void Run(TestShell* shell) {
+ wstring url = L"javascript:" + UTF8ToWide(script_);
+ shell->LoadURL(url.c_str());
+ }
+ private:
+ string script_;
+};
+
+void LayoutTestController::queueScript(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString())
+ work_queue_.AddWork(new WorkItemScript(args[0].ToString()));
+ result->SetNull();
+}
+
+class WorkItemLoad : public LayoutTestController::WorkItem {
+ public:
+ WorkItemLoad(const GURL& url, const string& target)
+ : url_(url), target_(target) {}
+ void Run(TestShell* shell) {
+ shell->LoadURLForFrame(UTF8ToWide(url_.spec()).c_str(),
+ UTF8ToWide(target_).c_str());
+ }
+ private:
+ GURL url_;
+ string target_;
+};
+
+void LayoutTestController::queueLoad(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ GURL current_url = shell_->webView()->GetMainFrame()->GetURL();
+ GURL full_url = current_url.Resolve(args[0].ToString());
+
+ string target = "";
+ if (args.size() > 1 && args[1].isString())
+ target = args[1].ToString();
+
+ work_queue_.AddWork(new WorkItemLoad(full_url, target));
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::objCIdentityIsEqual(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() < 2) {
+ // This is the best we can do to return an error.
+ result->SetNull();
+ return;
+ }
+ result->Set(args[0].isEqual(args[1]));
+}
+
+void LayoutTestController::Reset() {
+ if (shell_) {
+ shell_->webView()->MakeTextStandardSize();
+ shell_->webView()->SetTabKeyCyclesThroughElements(true);
+ }
+ dump_as_text_ = false;
+ dump_editing_callbacks_ = false;
+ dump_frame_load_callbacks_ = false;
+ dump_resource_load_callbacks_ = false;
+ dump_back_forward_list_ = false;
+ dump_child_frame_scroll_positions_ = false;
+ dump_child_frames_as_text_ = false;
+ dump_title_changes_ = false;
+ accepts_editing_ = true;
+ wait_until_done_ = false;
+ can_open_windows_ = false;
+ should_add_file_to_pasteboard_ = false;
+ globalFlag_.Set(false);
+
+ if (close_remaining_windows_) {
+ // Iterate through the window list and close everything except the original
+ // shell. We don't want to delete elements as we're iterating, so we copy
+ // to a temp vector first.
+ WindowList* windows = TestShell::windowList();
+ std::vector<HWND> windows_to_delete;
+ for (WindowList::iterator i = windows->begin(); i != windows->end(); ++i) {
+ if (*i != shell_->mainWnd())
+ windows_to_delete.push_back(*i);
+ }
+ DCHECK(windows_to_delete.size() + 1 == windows->size());
+ for (size_t i = 0; i < windows_to_delete.size(); ++i) {
+ DestroyWindow(windows_to_delete[i]);
+ }
+ DCHECK(windows->size() == 1);
+ } else {
+ // Reset the value
+ close_remaining_windows_ = true;
+ }
+ work_queue_.Reset();
+}
+
+void LayoutTestController::LocationChangeDone() {
+ // no more new work after the first complete load.
+ work_queue_.set_frozen(true);
+
+ if (!wait_until_done_)
+ work_queue_.ProcessWork();
+}
+
+void LayoutTestController::setCanOpenWindows(
+ const CppArgumentList& args, CppVariant* result) {
+ can_open_windows_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::setTabKeyCyclesThroughElements(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->webView()->SetTabKeyCyclesThroughElements(args[0].ToBoolean());
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::windowCount(
+ const CppArgumentList& args, CppVariant* result) {
+ int num_windows = static_cast<int>(TestShell::windowList()->size());
+ result->Set(num_windows);
+}
+
+void LayoutTestController::setCloseRemainingWindowsWhenComplete(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ close_remaining_windows_ = args[0].value.boolValue;
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setWindowIsKey(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->SetFocus(shell_->webViewHost(), args[0].value.boolValue);
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setUserStyleSheetEnabled(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetUserStyleSheetEnabled(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::setUserStyleSheetLocation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ GURL location(TestShell::RewriteLocalUrl(args[0].ToString()));
+ shell_->delegate()->SetUserStyleSheetLocation(location);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::execCommand(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ std::string command = args[0].ToString();
+ std::string value("");
+
+ // Ignore the second parameter (which is userInterface)
+ // since this command emulates a manual action.
+ if (args.size() >= 3 && args[2].isString())
+ value = args[2].ToString();
+
+ // Note: webkit's version does not return the boolean, so neither do we.
+ shell_->webView()->GetFocusedFrame()->ExecuteCoreCommandByName(command,
+ value);
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setUseDashboardCompatibilityMode(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetDashboardCompatibilityMode(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::setCustomPolicyDelegate(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetCustomPolicyDelegate(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::pathToLocalResource(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+ if (args.size() <= 0 || !args[0].isString())
+ return;
+
+ std::string url = args[0].ToString();
+ // Some layout tests use file://// which we resolve as a UNC path. Normalize
+ // them to just file:///.
+ while (StartsWithASCII(url, "file:////", false)) {
+ url = url.substr(0, 8) + url.substr(9);
+ }
+ GURL location(TestShell::RewriteLocalUrl(url));
+ result->Set(location.spec());
+}
+
+void LayoutTestController::addFileToPasteboardOnDrag(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+ should_add_file_to_pasteboard_ = true;
+}
+
+//
+// Unimplemented stubs
+//
+
+void LayoutTestController::dumpAsWebArchive(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::setMainFrameIsFirstResponder(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::dumpSelectionRect(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::display(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::testRepaint(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::repaintSweepHorizontally(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::clearBackForwardList(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::keepWebHistory(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::storeWebScriptObject(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::accessStoredWebScriptObject(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::objCClassNameOf(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::addDisallowedURL(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::setCallCloseOnWebViews(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::setPrivateBrowsingEnabled(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::fallbackMethod(
+ const CppArgumentList& args, CppVariant* result) {
+ std::wstring message(L"JavaScript ERROR: unknown method called on LayoutTestController");
+ if (shell_->interactive()) {
+ logging::LogMessage("CONSOLE:", 0).stream() << message;
+ } else {
+ printf("CONSOLE MESSAGE: %S\n", message.c_str());
+ }
+ result->SetNull();
+}
diff --git a/webkit/tools/test_shell/layout_test_controller.h b/webkit/tools/test_shell/layout_test_controller.h
new file mode 100644
index 0000000..5924a6e
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller.h
@@ -0,0 +1,306 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*
+ LayoutTestController class:
+ Bound to a JavaScript window.layoutTestController object using the
+ CppBoundClass::BindToJavascript(), this allows layout tests that are run in
+ the test_shell (or, in principle, any web page loaded into a client app built
+ with this class) to control various aspects of how the tests are run and what
+ sort of output they produce.
+*/
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
+
+#include <queue>
+
+#include "webkit/glue/cpp_bound_class.h"
+
+class TestShell;
+
+class LayoutTestController : public CppBoundClass {
+ public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ LayoutTestController(TestShell* shell);
+
+ // This function sets a flag that tells the test_shell to dump pages as
+ // plain text, rather than as a text representation of the renderer's state.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpAsText(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive test for each editing command. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpEditingCallbacks(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive test for each frame load callback. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpFrameLoadCallbacks(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print out a text
+ // representation of the back/forward list. It ignores all args.
+ void dumpBackForwardList(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print out the
+ // scroll offsets of the child frames. It ignores all args.
+ void dumpChildFrameScrollPositions(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to recursively
+ // dump all frames as plain text if the dumpAsText flag is set.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpChildFramesAsText(const CppArgumentList& args, CppVariant* result);
+
+ // When called with a boolean argument, this sets a flag that controls
+ // whether content-editable elements accept editing focus when an editing
+ // attempt is made. It ignores any additional arguments.
+ void setAcceptsEditing(const CppArgumentList& args, CppVariant* result);
+
+ // Functions for dealing with windows. By default we block all new windows.
+ void windowCount(const CppArgumentList& args, CppVariant* result);
+ void setCanOpenWindows(const CppArgumentList& args, CppVariant* result);
+ void setCloseRemainingWindowsWhenComplete(const CppArgumentList& args, CppVariant* result);
+
+ // By default, tests end when page load is complete. These methods are used
+ // to delay the completion of the test until notifyDone is called.
+ void waitUntilDone(const CppArgumentList& args, CppVariant* result);
+ void notifyDone(const CppArgumentList& args, CppVariant* result);
+
+ // Methods for adding actions to the work queue. Used in conjunction with
+ // waitUntilDone/notifyDone above.
+ void queueBackNavigation(const CppArgumentList& args, CppVariant* result);
+ void queueForwardNavigation(const CppArgumentList& args, CppVariant* result);
+ void queueReload(const CppArgumentList& args, CppVariant* result);
+ void queueScript(const CppArgumentList& args, CppVariant* result);
+ void queueLoad(const CppArgumentList& args, CppVariant* result);
+
+ // Although this is named "objC" to match the Mac version, it actually tests
+ // the identity of its two arguments in C++.
+ void objCIdentityIsEqual(const CppArgumentList& args, CppVariant* result);
+
+ // Gives focus to the window.
+ void setWindowIsKey(const CppArgumentList& args, CppVariant* result);
+
+ // Method that controls whether pressing Tab key cycles through page elements
+ // or inserts a '\t' char in text area
+ void setTabKeyCyclesThroughElements(const CppArgumentList& args, CppVariant* result);
+
+ // Passes through to WebPreferences which allows the user to have a custom
+ // style sheet.
+ void setUserStyleSheetEnabled(const CppArgumentList& args, CppVariant* result);
+ void setUserStyleSheetLocation(const CppArgumentList& args, CppVariant* result);
+
+ // Puts Webkit in "dashboard compatibility mode", which is used in obscure
+ // Mac-only circumstances. It's not really necessary, and will most likely
+ // never be used by Chrome, but some layout tests depend on its presence.
+ void setUseDashboardCompatibilityMode(const CppArgumentList& args, CppVariant* result);
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ void setCustomPolicyDelegate(const CppArgumentList& args, CppVariant* result);
+
+ // Converts a URL starting with file:///tmp/ to the local mapping.
+ void pathToLocalResource(const CppArgumentList& args, CppVariant* result);
+
+ // Sets a bool such that when a drag is started, we fill the drag clipboard
+ // with a fake file object.
+ void addFileToPasteboardOnDrag(const CppArgumentList& args, CppVariant* result);
+
+ // Executes an internal command (superset of document.execCommand() commands)
+ void execCommand(const CppArgumentList& args, CppVariant* result);;
+
+ // The following are only stubs. TODO(pamg): Implement any of these that
+ // are needed to pass the layout tests.
+ void dumpAsWebArchive(const CppArgumentList& args, CppVariant* result);
+ void dumpTitleChanges(const CppArgumentList& args, CppVariant* result);
+ void dumpResourceLoadCallbacks(const CppArgumentList& args, CppVariant* result);
+ void setMainFrameIsFirstResponder(const CppArgumentList& args, CppVariant* result);
+ void dumpSelectionRect(const CppArgumentList& args, CppVariant* result);
+ void display(const CppArgumentList& args, CppVariant* result);
+ void testRepaint(const CppArgumentList& args, CppVariant* result);
+ void repaintSweepHorizontally(const CppArgumentList& args, CppVariant* result);
+ void clearBackForwardList(const CppArgumentList& args, CppVariant* result);
+ void keepWebHistory(const CppArgumentList& args, CppVariant* result);
+ void storeWebScriptObject(const CppArgumentList& args, CppVariant* result);
+ void accessStoredWebScriptObject(const CppArgumentList& args, CppVariant* result);
+ void objCClassNameOf(const CppArgumentList& args, CppVariant* result);
+ void addDisallowedURL(const CppArgumentList& args, CppVariant* result);
+ void setCallCloseOnWebViews(const CppArgumentList& args, CppVariant* result);
+ void setPrivateBrowsingEnabled(const CppArgumentList& args, CppVariant* result);
+
+ // The fallback method is called when a nonexistent method is called on
+ // the layout test controller object.
+ // It is usefull to catch typos in the JavaScript code (a few layout tests
+ // do have typos in them) and it allows the script to continue running in
+ // that case (as the Mac does).
+ void fallbackMethod(const CppArgumentList& args, CppVariant* result);
+
+ public:
+ // The following methods are not exposed to JavaScript.
+ void SetWorkQueueFrozen(bool frozen) { work_queue_.set_frozen(frozen); }
+
+ bool ShouldDumpAsText() { return dump_as_text_; }
+ bool ShouldDumpEditingCallbacks() { return dump_editing_callbacks_; }
+ bool ShouldDumpFrameLoadCallbacks() { return dump_frame_load_callbacks_; }
+ void SetShouldDumpFrameLoadCallbacks(bool value) {
+ dump_frame_load_callbacks_ = value;
+ }
+ bool ShouldDumpResourceLoadCallbacks() {
+ return dump_resource_load_callbacks_;
+ }
+ bool ShouldDumpBackForwardList() { return dump_back_forward_list_; }
+ bool ShouldDumpTitleChanges() { return dump_title_changes_; }
+ bool ShouldDumpChildFrameScrollPositions() {
+ return dump_child_frame_scroll_positions_;
+ }
+ bool ShouldDumpChildFramesAsText() {
+ return dump_child_frames_as_text_;
+ }
+ bool AcceptsEditing() { return accepts_editing_; }
+ bool CanOpenWindows() { return can_open_windows_; }
+ bool ShouldAddFileToPasteboard() { return should_add_file_to_pasteboard_; }
+
+ // If we have queued events, fire them and then dump the test output.
+ // Otherwise, just dump the test output.
+ // Used by the layout tests for tests that span more than a single load.
+ // This is called by the test webview delegate when a page finishes
+ // loading (successful or not). Once all the work has been processed, we
+ // dump the test output.
+ void ProcessWork() { work_queue_.ProcessWork(); }
+
+ // Called by the webview delegate when the toplevel frame load is done.
+ void LocationChangeDone();
+
+ // Reinitializes all static values. The Reset() method should be called
+ // before the start of each test (currently from
+ // TestShell::RunFileTest).
+ void Reset();
+
+ // A single item in the work queue.
+ class WorkItem {
+ public:
+ virtual ~WorkItem() {};
+ virtual void Run(TestShell* shell) = 0;
+ };
+
+ // Used to clear the value of shell_ from test_shell_tests.
+ static void ClearShell() { shell_ = NULL; }
+
+ private:
+ friend class WorkItem;
+
+ // Helper class for managing events queued by methods like queueLoad or
+ // queueScript.
+ class WorkQueue {
+ public:
+ virtual ~WorkQueue();
+ void ProcessWork();
+
+ // Reset the state of the class between tests.
+ void Reset();
+
+ void AddWork(WorkItem* work);
+
+ void set_frozen(bool frozen) { frozen_ = frozen; }
+ bool empty() { return queue_.empty(); }
+
+ private:
+ std::queue<WorkItem*> queue_;
+ bool frozen_;
+ };
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+
+ // If true, the test_shell will produce a plain text dump rather than a
+ // text representation of the renderer.
+ static bool dump_as_text_;
+
+ // If true, the test_shell will write a descriptive line for each editing
+ // command.
+ static bool dump_editing_callbacks_;
+
+ // If true, the test_shell will output a descriptive line for each frame
+ // load callback.
+ static bool dump_frame_load_callbacks_;
+
+ // If true, the test_shell will output a descriptive line for each resource
+ // load callback.
+ static bool dump_resource_load_callbacks_;
+
+ // If true, the test_shell will produce a dump of the back forward list as
+ // well.
+ static bool dump_back_forward_list_;
+
+ // If true, the test_shell will print out the child frame scroll offsets as
+ // well.
+ static bool dump_child_frame_scroll_positions_;
+
+ // If true and if dump_as_text_ is true, the test_shell will recursively
+ // dump all frames as plain text.
+ static bool dump_child_frames_as_text_;
+
+ // If true, output a message when the page title is changed.
+ static bool dump_title_changes_;
+
+ // If true, the element will be treated as editable. This value is returned
+ // from various editing callbacks that are called just before edit operations
+ // are allowed.
+ static bool accepts_editing_;
+
+ // If true, new windows can be opened via javascript or by plugins. By
+ // default, set to false and can be toggled to true using
+ // setCanOpenWindows().
+ static bool can_open_windows_;
+
+ // When reset is called, go through and close all but the main test shell
+ // window. By default, set to true but toggled to false using
+ // setCloseRemainingWindowsWhenComplete().
+ static bool close_remaining_windows_;
+
+ // If true and a drag starts, adds a file to the drag&drop clipboard.
+ static bool should_add_file_to_pasteboard_;
+
+ // If true, don't dump output until notifyDone is called.
+ static bool wait_until_done_;
+
+ // To prevent infinite loops, only the first page of a test can add to a
+ // work queue (since we may well come back to that same page).
+ static bool work_queue_frozen_;
+
+
+ static WorkQueue work_queue_;
+
+ static CppVariant globalFlag_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/layout_test_controller_unittest.cc b/webkit/tools/test_shell/layout_test_controller_unittest.cc
new file mode 100644
index 0000000..4158b32
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <map>
+
+#include "webkit/tools/test_shell/layout_test_controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+// A subclass of LayoutTestController, with additional accessors.
+class TestLayoutTestController : public LayoutTestController {
+ public:
+ TestLayoutTestController() : LayoutTestController(NULL) {
+ }
+
+ size_t MethodCount() {
+ return methods_.size();
+ }
+
+ void Reset() {
+ LayoutTestController::Reset();
+ }
+};
+
+class LayoutTestControllerTest : public testing::Test {
+};
+} // namespace
+
+TEST(LayoutTestControllerTest, MethodMapIsInitialized) {
+ const char* test_methods[] = {
+ "dumpAsText",
+ "waitUntilDone",
+ "notifyDone",
+ "dumpEditingCallbacks",
+ "queueLoad",
+ "windowCount",
+ NULL
+ };
+ TestLayoutTestController controller;
+ for (const char** method = test_methods; *method; ++method) {
+ EXPECT_TRUE(controller.IsMethodRegistered(*method));
+ }
+
+ // One more case, to test our test.
+ EXPECT_FALSE(controller.IsMethodRegistered("nonexistent_method"));
+}
+
+TEST(LayoutTestControllerTest, DumpAsTextSetAndCleared) {
+ TestLayoutTestController controller;
+ CppArgumentList empty_args;
+ CppVariant ignored_result;
+ EXPECT_FALSE(controller.ShouldDumpAsText());
+ controller.dumpAsText(empty_args, &ignored_result);
+ EXPECT_TRUE(ignored_result.isNull());
+ EXPECT_TRUE(controller.ShouldDumpAsText());
+
+ // Don't worry about closing remaining windows when we call reset.
+ CppArgumentList args;
+ CppVariant bool_false;
+ bool_false.Set(false);
+ args.push_back(bool_false);
+ CppVariant result;
+ controller.setCloseRemainingWindowsWhenComplete(args, &result);
+
+ controller.Reset();
+ EXPECT_FALSE(controller.ShouldDumpAsText());
+}
+
+TEST(LayoutTestControllerTest, DumpChildFramesAsTextSetAndCleared) {
+ TestLayoutTestController controller;
+ CppArgumentList empty_args;
+ CppVariant ignored_result;
+ EXPECT_FALSE(controller.ShouldDumpChildFramesAsText());
+ controller.dumpChildFramesAsText(empty_args, &ignored_result);
+ EXPECT_TRUE(ignored_result.isNull());
+ EXPECT_TRUE(controller.ShouldDumpChildFramesAsText());
+
+ // Don't worry about closing remaining windows when we call reset.
+ CppArgumentList args;
+ CppVariant bool_false;
+ bool_false.Set(false);
+ args.push_back(bool_false);
+ CppVariant result;
+ controller.setCloseRemainingWindowsWhenComplete(args, &result);
+
+ controller.Reset();
+ EXPECT_FALSE(controller.ShouldDumpChildFramesAsText());
+}
diff --git a/webkit/tools/test_shell/node_leak_test.cc b/webkit/tools/test_shell/node_leak_test.cc
new file mode 100644
index 0000000..92ba447
--- /dev/null
+++ b/webkit/tools/test_shell/node_leak_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+#include "webkit/tools/test_shell/test_shell_switches.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+namespace {
+
+const wchar_t kTestUrlSwitch[] = L"test-url";
+
+// A test to help determine if any nodes have been leaked as a result of
+// visiting a given URL. If enabled in WebCore, the number of leaked nodes
+// can be printed upon termination. This is only enabled in debug builds, so
+// it only makes sense to run this using a debug build.
+//
+// It will load a URL, visit about:blank, and then perform garbage collection.
+// The number of remaining (potentially leaked) nodes will be printed on exit.
+class NodeLeakTest : public TestShellTest {
+ public:
+ virtual void SetUp() {
+ CommandLine parsed_command_line;
+
+ std::wstring js_flags =
+ parsed_command_line.GetSwitchValue(test_shell::kJavaScriptFlags);
+ CommandLine::AppendSwitch(&js_flags, L"expose-gc");
+ webkit_glue::SetJavaScriptFlags(js_flags);
+
+ std::wstring cache_path =
+ parsed_command_line.GetSwitchValue(test_shell::kCacheDir);
+ if (cache_path.empty()) {
+ PathService::Get(base::DIR_EXE, &cache_path);
+ file_util::AppendToPath(&cache_path, L"cache");
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kTestShellTimeOut)) {
+ const std::wstring timeout_str = parsed_command_line.GetSwitchValue(
+ test_shell::kTestShellTimeOut);
+ int timeout_ms = static_cast<int>(StringToInt64(timeout_str.c_str()));
+ if (timeout_ms > 0)
+ TestShell::SetFileTestTimeout(timeout_ms);
+ }
+
+ // Optionally use playback mode (for instance if running automated tests).
+ net::HttpCache::Mode mode =
+ parsed_command_line.HasSwitch(test_shell::kPlaybackMode) ?
+ net::HttpCache::PLAYBACK : net::HttpCache::NORMAL;
+ SimpleResourceLoaderBridge::Init(
+ new TestShellRequestContext(cache_path, mode));
+
+ TestShellTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+
+ SimpleResourceLoaderBridge::Shutdown();
+ }
+
+ void NavigateToURL(const std::wstring& test_url) {
+ test_shell_->LoadURL(test_url.c_str());
+ test_shell_->WaitTestFinished();
+
+ // Depends on TestShellTests::TearDown to load blank page and
+ // the TestShell destructor to call garbage collection.
+ }
+};
+
+} // namespace
+
+TEST_F(NodeLeakTest, TestURL) {
+ CommandLine parsed_command_line;
+
+ if (parsed_command_line.HasSwitch(kTestUrlSwitch)) {
+ NavigateToURL(parsed_command_line.GetSwitchValue(kTestUrlSwitch).c_str());
+ }
+}
diff --git a/webkit/tools/test_shell/plugin_tests.cc b/webkit/tools/test_shell/plugin_tests.cc
new file mode 100644
index 0000000..bfde855
--- /dev/null
+++ b/webkit/tools/test_shell/plugin_tests.cc
@@ -0,0 +1,147 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_util.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+static const char kTestCompleteCookie[] = "status";
+static const char kTestCompleteSuccess[] = "OK";
+
+// Provides functionality for creating plugin tests.
+class PluginTest : public TestShellTest {
+ // A basic URLRequestContext that only provides an in-memory cookie store.
+ class RequestContext : public TestURLRequestContext {
+ public:
+ RequestContext() {
+ cookie_store_ = new CookieMonster();
+ }
+
+ virtual ~RequestContext() {
+ delete cookie_store_;
+ }
+ };
+
+ public:
+ PluginTest() {}
+ ~PluginTest() {}
+
+ void NavigateToURL(const std::wstring& test_url) {
+ ASSERT_TRUE(file_util::PathExists(test_url));
+ test_url_ = net_util::FilePathToFileURL(test_url);
+ test_shell_->LoadURL(test_url.c_str());
+ }
+
+ // Waits for the test case to finish.
+ // ASSERTS if there are test failures.
+ void WaitForFinish(const std::string &name, const std::string &id) {
+ test_shell_->WaitTestFinished();
+
+ std::string cookies =
+ request_context_->cookie_store()->GetCookies(test_url_);
+ EXPECT_FALSE(cookies.empty());
+
+ std::string cookieName = name;
+ cookieName.append(".");
+ cookieName.append(id);
+ cookieName.append(".");
+ cookieName.append(kTestCompleteCookie);
+ cookieName.append("=");
+ std::string::size_type idx = cookies.find(cookieName);
+ std::string cookie;
+ if (idx != std::string::npos) {
+ cookies.erase(0, idx + cookieName.length());
+ cookie = cookies.substr(0, cookies.find(";"));
+ }
+
+ EXPECT_EQ(kTestCompleteSuccess, cookie);
+ }
+
+ protected:
+ virtual void SetUp() {
+ // We need to copy our test-plugin into the plugins directory so that
+ // the test can load it.
+ std::wstring current_directory;
+ PathService::Get(base::DIR_EXE, &current_directory);
+ std::wstring plugin_src = current_directory + L"\\npapi_test_plugin.dll";
+ ASSERT_TRUE(file_util::PathExists(plugin_src));
+
+ plugin_dll_path_ = current_directory + L"\\plugins";
+ ::CreateDirectory(plugin_dll_path_.c_str(), NULL);
+
+ plugin_dll_path_ += L"\\npapi_test_plugin.dll";
+ ASSERT_TRUE(CopyFile(plugin_src.c_str(), plugin_dll_path_.c_str(), FALSE));
+
+ // The plugin list has to be refreshed to ensure that the npapi_test_plugin
+ // is loaded by webkit.
+ std::vector<WebPluginInfo> plugin_list;
+ bool refresh = true;
+ NPAPI::PluginList::Singleton()->GetPlugins(refresh, &plugin_list);
+
+ TestShellTest::SetUp();
+
+ plugin_data_dir_ = data_dir_;
+ file_util::AppendToPath(&plugin_data_dir_, L"plugin_tests");
+ ASSERT_TRUE(file_util::PathExists(plugin_data_dir_));
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+
+ // TODO(iyengar) The DeleteFile call fails in some cases as the plugin is
+ // still in use. Needs more investigation.
+ ::DeleteFile(plugin_dll_path_.c_str());
+ }
+
+ std::wstring plugin_data_dir_;
+ std::wstring plugin_dll_path_;
+ RequestContext* request_context_;
+ GURL test_url_;
+};
+
+TEST_F(PluginTest, DISABLED_VerifyPluginWindowRect) {
+ std::wstring test_url = GetTestURL(plugin_data_dir_,
+ L"verify_plugin_window_rect.html");
+ NavigateToURL(test_url);
+ WaitForFinish("checkwindowrect", "1");
+}
diff --git a/webkit/tools/test_shell/resource.h b/webkit/tools/test_shell/resource.h
new file mode 100644
index 0000000..d56339e
--- /dev/null
+++ b/webkit/tools/test_shell/resource.h
@@ -0,0 +1,38 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by test_shell.rc
+//
+
+#define IDS_APP_TITLE 103
+
+#define IDR_MAINFRAME 128
+#define IDD_TESTSHELL_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDM_DUMP_BODY_TEXT 110
+#define IDM_DUMP_RENDER_TREE 111
+#define IDM_SHOW_WEB_INSPECTOR 112
+#define IDI_TESTSHELL 107
+#define IDI_SMALL 108
+#define IDC_TESTSHELL 109
+#define IDC_MYICON 2
+#define IDC_NAV_BACK 1001
+#define IDC_NAV_FORWARD 1002
+#define IDC_NAV_RELOAD 1003
+#define IDC_NAV_STOP 1004
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+
+#define _APS_NO_MFC 130
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 113
+#endif
+#endif
diff --git a/webkit/tools/test_shell/resources/AHEM____.TTF b/webkit/tools/test_shell/resources/AHEM____.TTF
new file mode 100644
index 0000000..ac81cb0
--- /dev/null
+++ b/webkit/tools/test_shell/resources/AHEM____.TTF
Binary files differ
diff --git a/webkit/tools/test_shell/resources/README.txt b/webkit/tools/test_shell/resources/README.txt
new file mode 100644
index 0000000..c227a5d
--- /dev/null
+++ b/webkit/tools/test_shell/resources/README.txt
@@ -0,0 +1,24 @@
+missingImage.gif was created from Webkit data: WebCore/Resources/missingImage.tiff
+
+Licence text for missingImage.tiff from which missingImage.gif was generated:
+
+Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/webkit/tools/test_shell/resources/missingImage.gif b/webkit/tools/test_shell/resources/missingImage.gif
new file mode 100644
index 0000000..0f7215d
--- /dev/null
+++ b/webkit/tools/test_shell/resources/missingImage.gif
Binary files differ
diff --git a/webkit/tools/test_shell/resources/small.ico b/webkit/tools/test_shell/resources/small.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/webkit/tools/test_shell/resources/small.ico
Binary files differ
diff --git a/webkit/tools/test_shell/resources/test_shell.ico b/webkit/tools/test_shell/resources/test_shell.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/webkit/tools/test_shell/resources/test_shell.ico
Binary files differ
diff --git a/webkit/tools/test_shell/resources/test_shell.rc b/webkit/tools/test_shell/resources/test_shell.rc
new file mode 100644
index 0000000..a8fe86e
--- /dev/null
+++ b/webkit/tools/test_shell/resources/test_shell.rc
@@ -0,0 +1,133 @@
+//Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+
+IDI_TESTSHELL ICON "test_shell.ico"
+IDI_SMALL ICON "small.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDC_TESTSHELL MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", IDM_EXIT
+ END
+ POPUP "&Debug"
+ BEGIN
+ MENUITEM "Dump body text...", IDM_DUMP_BODY_TEXT
+ MENUITEM "Dump render tree...", IDM_DUMP_RENDER_TREE
+ MENUITEM "Show web inspector...", IDM_SHOW_WEB_INSPECTOR
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About ...", IDM_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDC_TESTSHELL ACCELERATORS
+BEGIN
+ "?", IDM_ABOUT, ASCII, ALT
+ "/", IDM_ABOUT, ASCII, ALT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG 22, 17, 230, 75
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "About"
+FONT 8, "System"
+BEGIN
+ ICON IDI_TESTSHELL,IDC_MYICON,14,9,16,16
+ LTEXT "TestShell Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX
+ LTEXT "Copyright (C) 2006",IDC_STATIC,49,20,119,8
+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDC_TESTSHELL "TESTSHELL"
+ IDS_APP_TITLE "TestShell"
+END
+
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/webkit/tools/test_shell/run_all_tests.cc b/webkit/tools/test_shell/run_all_tests.cc
new file mode 100644
index 0000000..05cdc43
--- /dev/null
+++ b/webkit/tools/test_shell/run_all_tests.cc
@@ -0,0 +1,85 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Run all of our test shell tests. This is just an entry point
+// to kick off gTest's RUN_ALL_TESTS().
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "base/icu_util.h"
+#include "base/message_loop.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char* TestShellTest::kJavascriptDelayExitScript =
+ "<script>"
+ "window.layoutTestController.waitUntilDone();"
+ "window.addEventListener('load', function() {"
+ " var x = document.body.clientWidth;" // Force a document layout
+ " window.layoutTestController.notifyDone();"
+ "});"
+ "</script>";
+
+int main(int argc, char* argv[]) {
+ TestShell::InitLogging(true); // suppress error dialogs
+
+ // Initialize test shell in non-interactive mode, which will let us load one
+ // request than automatically quit.
+ TestShell::InitializeTestShell(false);
+
+ // Some of the individual tests wind up calling TestShell::WaitTestFinished
+ // which has a timeout in it. For these tests, we don't care about a timeout
+ // so just set it to be a really large number. This is necessary because
+ // when running under Purify, we were hitting those timeouts.
+ TestShell::SetFileTestTimeout(USER_TIMER_MAXIMUM);
+
+ // Allocate a message loop for this thread. Although it is not used
+ // directly, its constructor sets up some necessary state.
+ MessageLoop main_message_loop;
+
+ // Load ICU data tables
+ icu_util::Initialize();
+
+ INITCOMMONCONTROLSEX InitCtrlEx;
+
+ InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
+ InitCommonControlsEx(&InitCtrlEx);
+
+ // Run the actual tests
+ testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+
+ TestShell::ShutdownTestShell();
+ TestShell::CleanupLogging();
+ return result;
+}
diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
new file mode 100644
index 0000000..e8dcbf2
--- /dev/null
+++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
@@ -0,0 +1,584 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file contains an implementation of the ResourceLoaderBridge class.
+// The class is implemented using URLRequest, meaning it is a "simple" version
+// that directly issues requests. The more complicated one used in the
+// browser uses IPC.
+//
+// Because URLRequest only provides an asynchronous resource loading API, this
+// file makes use of URLRequest from a background IO thread. Requests for
+// cookies and synchronously loaded resources result in the main thread of the
+// application blocking until the IO thread completes the operation. (See
+// GetCookies and SyncLoad)
+//
+// Main thread IO thread
+// ----------- ---------
+// ResourceLoaderBridge <---o---------> RequestProxy (normal case)
+// \ -> URLRequest
+// o-------> SyncRequestProxy (synchronous case)
+// -> URLRequest
+// SetCookie <------------------------> CookieSetter
+// -> net_util::SetCookie
+// GetCookies <-----------------------> CookieGetter
+// -> net_util::GetCookies
+//
+// NOTE: The implementation in this file may be used to have WebKit fetch
+// resources in-process. For example, it is handy for building a single-
+// process WebKit embedding (e.g., test_shell) that can use URLRequest to
+// perform URL loads. See renderer/resource_dispatcher.h for details on an
+// alternate implementation that defers fetching to another process.
+
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_util.h"
+#include "net/base/upload_data.h"
+#include "net/url_request/url_request.h"
+#include "webkit/glue/resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+
+using webkit_glue::ResourceLoaderBridge;
+using net::HttpResponseHeaders;
+
+namespace {
+
+//-----------------------------------------------------------------------------
+
+URLRequestContext* request_context = NULL;
+Thread* io_thread = NULL;
+
+class IOThread : public Thread {
+ public:
+ IOThread() : Thread("IOThread") {
+ }
+
+ ~IOThread() {
+ // We cannot rely on our base class to stop the thread since we want our
+ // CleanUp function to run.
+ Stop();
+ }
+
+ virtual void CleanUp() {
+ if (request_context) {
+ request_context->Release();
+ request_context = NULL;
+ }
+ }
+};
+
+bool EnsureIOThread() {
+ if (io_thread)
+ return true;
+
+ if (!request_context)
+ SimpleResourceLoaderBridge::Init(NULL);
+
+ io_thread = new IOThread();
+ return io_thread->Start();
+}
+
+//-----------------------------------------------------------------------------
+
+struct RequestParams {
+ std::string method;
+ GURL url;
+ GURL policy_url;
+ GURL referrer;
+ std::string headers;
+ int load_flags;
+ scoped_refptr<net::UploadData> upload;
+};
+
+// The RequestProxy does most of its work on the IO thread. The Start and
+// Cancel methods are proxied over to the IO thread, where an URLRequest object
+// is instantiated.
+class RequestProxy : public URLRequest::Delegate,
+ public base::RefCountedThreadSafe<RequestProxy> {
+ public:
+ // Takes ownership of the params.
+ RequestProxy() {
+ }
+
+ virtual ~RequestProxy() {
+ // If we have a request, then we'd better be on the io thread!
+ DCHECK(!request_.get() ||
+ MessageLoop::current() == io_thread->message_loop());
+ }
+
+ void DropPeer() {
+ peer_ = NULL;
+ }
+
+ void Start(ResourceLoaderBridge::Peer* peer, RequestParams* params) {
+ peer_ = peer;
+ owner_loop_ = MessageLoop::current();
+
+ // proxy over to the io thread
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncStart, params));
+ }
+
+ void Cancel() {
+ // proxy over to the io thread
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncCancel));
+ }
+
+ protected:
+ // --------------------------------------------------------------------------
+ // The following methods are called on the owner's thread in response to
+ // various URLRequest callbacks. The event hooks, defined below, trigger
+ // these methods asynchronously.
+
+ void NotifyReceivedRedirect(const GURL& new_url) {
+ if (peer_)
+ peer_->OnReceivedRedirect(new_url);
+ }
+
+ void NotifyReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info) {
+ if (peer_)
+ peer_->OnReceivedResponse(info);
+ }
+
+ void NotifyReceivedData(int bytes_read) {
+ if (!peer_)
+ return;
+
+ // Make a local copy of buf_, since AsyncReadData reuses it.
+ scoped_array<char> buf_copy(new char[bytes_read]);
+ memcpy(buf_copy.get(), buf_, bytes_read);
+
+ // Continue reading more data into buf_
+ // Note: Doing this before notifying our peer ensures our load events get
+ // dispatched in a manner consistent with DumpRenderTree (and also avoids a
+ // race condition). If the order of the next 2 functions were reversed, the
+ // peer could generate new requests in reponse to the received data, which
+ // when run on the io thread, could race against this function in doing
+ // another InvokeLater. See bug 769249.
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncReadData));
+
+ peer_->OnReceivedData(buf_copy.get(), bytes_read);
+ }
+
+ void NotifyCompletedRequest(const URLRequestStatus& status) {
+ if (peer_) {
+ peer_->OnCompletedRequest(status);
+ DropPeer(); // ensure no further notifications
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // The following methods are called on the io thread. They correspond to
+ // actions performed on the owner's thread.
+
+ void AsyncStart(RequestParams* params) {
+ request_.reset(new URLRequest(params->url, this));
+ request_->set_method(params->method);
+ request_->set_policy_url(params->policy_url);
+ request_->set_referrer(params->referrer.spec());
+ request_->SetExtraRequestHeaders(params->headers);
+ request_->set_load_flags(params->load_flags);
+ request_->set_upload(params->upload.get());
+ request_->set_context(request_context);
+ request_->Start();
+
+ delete params;
+ }
+
+ void AsyncCancel() {
+ // This can be null in cases where the request is already done.
+ if (!request_.get())
+ return;
+
+ request_->Cancel();
+ Done();
+ }
+
+ void AsyncReadData() {
+ // This can be null in cases where the request is already done.
+ if (!request_.get())
+ return;
+
+ if (request_->status().is_success()) {
+ int bytes_read;
+ if (request_->Read(buf_, sizeof(buf_), &bytes_read) && bytes_read) {
+ OnReceivedData(bytes_read);
+ } else if (!request_->status().is_io_pending()) {
+ Done();
+ } // else wait for OnReadCompleted
+ } else {
+ Done();
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // The following methods are event hooks (corresponding to URLRequest
+ // callbacks) that run on the IO thread. They are designed to be overridden
+ // by the SyncRequestProxy subclass.
+
+ virtual void OnReceivedRedirect(const GURL& new_url) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedRedirect, new_url));
+ }
+
+ virtual void OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedResponse, info));
+ }
+
+ virtual void OnReceivedData(int bytes_read) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedData, bytes_read));
+ }
+
+ virtual void OnCompletedRequest(const URLRequestStatus& status) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyCompletedRequest, status));
+ }
+
+ // --------------------------------------------------------------------------
+ // URLRequest::Delegate implementation:
+
+ virtual void OnReceivedRedirect(URLRequest* request,
+ const GURL& new_url) {
+ DCHECK(request->status().is_success());
+ OnReceivedRedirect(new_url);
+ }
+
+ virtual void OnResponseStarted(URLRequest* request) {
+ if (request->status().is_success()) {
+ ResourceLoaderBridge::ResponseInfo info;
+ info.request_time = request->request_time();
+ info.response_time = request->response_time();
+ info.headers = request->response_headers();
+ request->GetMimeType(&info.mime_type);
+ request->GetCharset(&info.charset);
+ OnReceivedResponse(info);
+ AsyncReadData(); // start reading
+ } else {
+ Done();
+ }
+ }
+
+ virtual void OnReadCompleted(URLRequest* request, int bytes_read) {
+ if (request->status().is_success() && bytes_read > 0) {
+ OnReceivedData(bytes_read);
+ } else {
+ Done();
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Helpers and data:
+
+ void Done() {
+ DCHECK(request_.get());
+ OnCompletedRequest(request_->status());
+ request_.reset(); // destroy on the io thread
+ }
+
+ scoped_ptr<URLRequest> request_;
+
+ // Size of our async IO data buffers
+ static const int kDataSize = 16*1024;
+
+ // read buffer for async IO
+ char buf_[kDataSize];
+
+ MessageLoop* owner_loop_;
+
+ // This is our peer in WebKit (implemented as ResourceHandleInternal). We do
+ // not manage its lifetime, and we may only access it from the owner's
+ // message loop (owner_loop_).
+ ResourceLoaderBridge::Peer* peer_;
+};
+
+//-----------------------------------------------------------------------------
+
+class SyncRequestProxy : public RequestProxy {
+ public:
+ explicit SyncRequestProxy(ResourceLoaderBridge::SyncLoadResponse* result)
+ : event_(::CreateEvent(NULL, TRUE, FALSE, NULL)),
+ result_(result) {
+ }
+
+ virtual ~SyncRequestProxy() {
+ CloseHandle(event_);
+ }
+
+ HANDLE event() const {
+ return event_;
+ }
+
+ // --------------------------------------------------------------------------
+ // Event hooks that run on the IO thread:
+
+ virtual void OnReceivedRedirect(const GURL& new_url) {
+ result_->url = new_url;
+ }
+
+ virtual void OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ *static_cast<ResourceLoaderBridge::ResponseInfo*>(result_) = info;
+ }
+
+ virtual void OnReceivedData(int bytes_read) {
+ result_->data.append(buf_, bytes_read);
+ AsyncReadData(); // read more (may recurse)
+ }
+
+ virtual void OnCompletedRequest(const URLRequestStatus& status) {
+ result_->status = status;
+ ::SetEvent(event_);
+ }
+
+ private:
+ ResourceLoaderBridge::SyncLoadResponse* result_;
+ HANDLE event_;
+};
+
+//-----------------------------------------------------------------------------
+
+class ResourceLoaderBridgeImpl : public ResourceLoaderBridge {
+ public:
+ ResourceLoaderBridgeImpl(const std::string& method,
+ const GURL& url,
+ const GURL& policy_url,
+ const GURL& referrer,
+ const std::string& headers,
+ int load_flags)
+ : params_(new RequestParams),
+ proxy_(NULL) {
+ params_->method = method;
+ params_->url = url;
+ params_->policy_url = policy_url;
+ params_->referrer = referrer;
+ params_->headers = headers;
+ params_->load_flags = load_flags;
+ }
+
+ virtual ~ResourceLoaderBridgeImpl() {
+ if (proxy_) {
+ proxy_->DropPeer();
+ // Let the proxy die on the IO thread
+ io_thread->message_loop()->ReleaseSoon(FROM_HERE, proxy_);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // ResourceLoaderBridge implementation:
+
+ virtual void AppendDataToUpload(const char* data, int data_len) {
+ DCHECK(params_.get());
+ if (!params_->upload)
+ params_->upload = new net::UploadData();
+ params_->upload->AppendBytes(data, data_len);
+ }
+
+ virtual void AppendFileRangeToUpload(const std::wstring& file_path,
+ uint64 offset, uint64 length) {
+ DCHECK(params_.get());
+ if (!params_->upload)
+ params_->upload = new net::UploadData();
+ params_->upload->AppendFileRange(file_path, offset, length);
+ }
+
+ virtual bool Start(Peer* peer) {
+ DCHECK(!proxy_);
+
+ if (!EnsureIOThread())
+ return false;
+
+ proxy_ = new RequestProxy();
+ proxy_->AddRef();
+
+ proxy_->Start(peer, params_.release());
+
+ return true; // Any errors will be reported asynchronously.
+ }
+
+ virtual void Cancel() {
+ DCHECK(proxy_);
+ proxy_->Cancel();
+ }
+
+ virtual void SetDefersLoading(bool value) {
+ // TODO(darin): implement me
+ }
+
+ virtual void SyncLoad(SyncLoadResponse* response) {
+ DCHECK(!proxy_);
+
+ if (!EnsureIOThread())
+ return;
+
+ // this may change as the result of a redirect
+ response->url = params_->url;
+
+ proxy_ = new SyncRequestProxy(response);
+ proxy_->AddRef();
+
+ proxy_->Start(NULL, params_.release());
+
+ HANDLE event = static_cast<SyncRequestProxy*>(proxy_)->event();
+
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
+ NOTREACHED();
+ }
+
+ private:
+ // Ownership of params_ is transfered to the proxy when the proxy is created.
+ scoped_ptr<RequestParams> params_;
+
+ // The request proxy is allocated when we start the request, and then it
+ // sticks around until this ResourceLoaderBridge is destroyed.
+ RequestProxy* proxy_;
+};
+
+//-----------------------------------------------------------------------------
+
+class CookieSetter : public base::RefCountedThreadSafe<CookieSetter> {
+ public:
+ void Set(const GURL& url, const std::string& cookie) {
+ DCHECK(MessageLoop::current() == io_thread->message_loop());
+ request_context->cookie_store()->SetCookie(url, cookie);
+ }
+};
+
+class CookieGetter : public base::RefCountedThreadSafe<CookieGetter> {
+ public:
+ CookieGetter()
+ : event_(::CreateEvent(NULL, FALSE, FALSE, NULL)) {
+ }
+
+ ~CookieGetter() {
+ CloseHandle(event_);
+ }
+
+ void Get(const GURL& url) {
+ result_ = request_context->cookie_store()->GetCookies(url);
+ SetEvent(event_);
+ }
+
+ std::string GetResult() {
+ if (WaitForSingleObject(event_, INFINITE) != WAIT_OBJECT_0)
+ NOTREACHED();
+ return result_;
+ }
+
+ private:
+ HANDLE event_;
+ std::string result_;
+};
+
+} // anonymous namespace
+
+//-----------------------------------------------------------------------------
+
+namespace webkit_glue {
+
+// factory function
+ResourceLoaderBridge* ResourceLoaderBridge::Create(
+ WebFrame* webframe,
+ const std::string& method,
+ const GURL& url,
+ const GURL& policy_url,
+ const GURL& referrer,
+ const std::string& headers,
+ int load_flags,
+ int origin_pid,
+ ResourceType::Type request_type,
+ bool mixed_contents) {
+ return new ResourceLoaderBridgeImpl(method, url, policy_url, referrer,
+ headers, load_flags);
+}
+
+void SetCookie(const GURL& url, const GURL& policy_url,
+ const std::string& cookie) {
+ // Proxy to IO thread to synchronize w/ network loading.
+
+ if (!EnsureIOThread()) {
+ NOTREACHED();
+ return;
+ }
+
+ scoped_refptr<CookieSetter> cookie_setter = new CookieSetter();
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ cookie_setter.get(), &CookieSetter::Set, url, cookie));
+}
+
+std::string GetCookies(const GURL& url, const GURL& policy_url) {
+ // Proxy to IO thread to synchronize w/ network loading
+
+ if (!EnsureIOThread()) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ scoped_refptr<CookieGetter> getter = new CookieGetter();
+
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ getter.get(), &CookieGetter::Get, url));
+
+ return getter->GetResult();
+}
+
+} // namespace webkit_glue
+
+//-----------------------------------------------------------------------------
+
+// static
+void SimpleResourceLoaderBridge::Init(URLRequestContext* context) {
+ // Make sure to stop any existing IO thread since it may be using the
+ // current request context.
+ Shutdown();
+
+ if (context) {
+ request_context = context;
+ } else {
+ request_context = new TestShellRequestContext();
+ }
+ request_context->AddRef();
+}
+
+// static
+void SimpleResourceLoaderBridge::Shutdown() {
+ if (io_thread) {
+ delete io_thread;
+ io_thread = NULL;
+
+ DCHECK(!request_context) << "should have been nulled by thread dtor";
+ }
+}
diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.h b/webkit/tools/test_shell/simple_resource_loader_bridge.h
new file mode 100644
index 0000000..3c06594
--- /dev/null
+++ b/webkit/tools/test_shell/simple_resource_loader_bridge.h
@@ -0,0 +1,55 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
+
+#include "base/ref_counted.h"
+
+class URLRequestContext;
+
+class SimpleResourceLoaderBridge {
+ public:
+ // Call this function to initialize the simple resource loader bridge. If
+ // the given context is null, then a default TestShellRequestContext will be
+ // instantiated. Otherwise, a reference is taken to the given request
+ // context, which will be released when Shutdown is called. The caller
+ // should not hold another reference to the request context! It is safe to
+ // call this function multiple times.
+ //
+ // NOTE: If this function is not called, then a default request context will
+ // be initialized lazily.
+ //
+ static void Init(URLRequestContext* context);
+
+ // Call this function to shutdown the simple resource loader bridge.
+ static void Shutdown();
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
diff --git a/webkit/tools/test_shell/temp/navigation_controller_base.cc b/webkit/tools/test_shell/temp/navigation_controller_base.cc
new file mode 100644
index 0000000..287e870
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_controller_base.cc
@@ -0,0 +1,301 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/temp/navigation_controller_base.h"
+
+#include <algorithm>
+
+#include "webkit/tools/test_shell/temp/navigation_entry.h"
+#include "base/logging.h"
+
+NavigationControllerBase::NavigationControllerBase()
+ : pending_entry_(NULL),
+ last_committed_entry_index_(-1),
+ pending_entry_index_(-1) {
+}
+
+NavigationControllerBase::~NavigationControllerBase() {
+ // NOTE: This does NOT invoke Reset as Reset is virtual.
+ ResetInternal();
+}
+
+void NavigationControllerBase::Reset() {
+ ResetInternal();
+
+ last_committed_entry_index_ = -1;
+}
+
+NavigationEntry* NavigationControllerBase::GetActiveEntry() const {
+ NavigationEntry* entry = pending_entry_;
+ if (!entry)
+ entry = GetLastCommittedEntry();
+ return entry;
+}
+
+int NavigationControllerBase::GetCurrentEntryIndex() const {
+ if (pending_entry_index_ != -1)
+ return pending_entry_index_;
+ return last_committed_entry_index_;
+}
+
+NavigationEntry* NavigationControllerBase::GetLastCommittedEntry() const {
+ if (last_committed_entry_index_ == -1)
+ return NULL;
+ return entries_[last_committed_entry_index_];
+}
+
+int NavigationControllerBase::GetEntryIndexWithPageID(
+ TabContentsType type, int32 page_id) const {
+ for (int i = static_cast<int>(entries_.size())-1; i >= 0; --i) {
+ if (entries_[i]->GetType() == type && entries_[i]->GetPageID() == page_id)
+ return i;
+ }
+ return -1;
+}
+
+NavigationEntry* NavigationControllerBase::GetEntryWithPageID(
+ TabContentsType type, int32 page_id) const {
+ int index = GetEntryIndexWithPageID(type, page_id);
+ return (index != -1) ? entries_[index] : NULL;
+}
+
+NavigationEntry* NavigationControllerBase::GetEntryAtOffset(int offset) const {
+ int index = last_committed_entry_index_ + offset;
+ if (index < 0 || index >= GetEntryCount())
+ return NULL;
+
+ return entries_[index];
+}
+
+bool NavigationControllerBase::CanStop() const {
+ // TODO(darin): do we have something pending that we can stop?
+ return false;
+}
+
+bool NavigationControllerBase::CanGoBack() const {
+ return entries_.size() > 1 && GetCurrentEntryIndex() > 0;
+}
+
+bool NavigationControllerBase::CanGoForward() const {
+ int index = GetCurrentEntryIndex();
+ return index >= 0 && index < (static_cast<int>(entries_.size()) - 1);
+}
+
+void NavigationControllerBase::GoBack() {
+ DCHECK(CanGoBack());
+
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = current_index - 1;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoForward() {
+ DCHECK(CanGoForward());
+
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = current_index + 1;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoToIndex(int index) {
+ DCHECK(index >= 0);
+ DCHECK(index < static_cast<int>(entries_.size()));
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = index;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoToOffset(int offset) {
+ int index = last_committed_entry_index_ + offset;
+ if (index < 0 || index >= GetEntryCount())
+ return;
+
+ GoToIndex(index);
+}
+
+void NavigationControllerBase::Stop() {
+ DCHECK(CanStop());
+
+ // TODO(darin): we probably want to just call Stop on the active tab
+ // contents, but should we also call DiscardPendingEntry?
+ NOTREACHED() << "implement me";
+}
+
+void NavigationControllerBase::Reload() {
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ // If we are no where, then we can't reload. TODO(darin): We should add a
+ // CanReload method.
+ if (current_index == -1)
+ return;
+
+ DiscardPendingEntryInternal();
+
+ pending_entry_index_ = current_index;
+ entries_[pending_entry_index_]->SetTransition(PageTransition::RELOAD);
+ NavigateToPendingEntry(true);
+}
+
+void NavigationControllerBase::LoadEntry(NavigationEntry* entry) {
+ // When navigating to a new page, we don't know for sure if we will actually
+ // end up leaving the current page. The new page load could for example
+ // result in a download or a 'no content' response (e.g., a mailto: URL).
+
+ DiscardPendingEntryInternal();
+ pending_entry_ = entry;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::DidNavigateToEntry(NavigationEntry* entry) {
+ // If the entry is that of a page with PageID larger than any this Tab has
+ // seen before, then consider it a new navigation.
+ if (entry->GetPageID() > GetMaxPageID()) {
+ InsertEntry(entry);
+ return;
+ }
+
+ // Otherwise, we just need to update an existing entry with matching PageID.
+ // If the existing entry corresponds to the entry which is pending, then we
+ // must update the current entry index accordingly. When navigating to the
+ // same URL, a new PageID is not created.
+
+ int existing_entry_index = GetEntryIndexWithPageID(entry->GetType(),
+ entry->GetPageID());
+ NavigationEntry* existing_entry =
+ (existing_entry_index != -1) ? entries_[existing_entry_index] : NULL;
+ if (!existing_entry) {
+ // No existing entry, then simply ignore this navigation!
+ DLOG(WARNING) << "ignoring navigation for page: " << entry->GetPageID();
+ } else if (existing_entry == pending_entry_) {
+ // The given entry might provide a new URL... e.g., navigating back to a
+ // page in session history could have resulted in a new client redirect.
+ existing_entry->SetURL(entry->GetURL());
+ existing_entry->SetContentState(entry->GetContentState());
+ last_committed_entry_index_ = pending_entry_index_;
+ pending_entry_index_ = -1;
+ pending_entry_ = NULL;
+ IndexOfActiveEntryChanged();
+ } else if (pending_entry_ && pending_entry_->GetPageID() == -1 &&
+ pending_entry_->GetURL() == existing_entry->GetURL()) {
+ // Not a new navigation
+ DiscardPendingEntry();
+ } else {
+ // The given entry might provide a new URL... e.g., navigating to a page
+ // might result in a client redirect, which should override the URL of the
+ // existing entry.
+ existing_entry->SetURL(entry->GetURL());
+ existing_entry->SetContentState(entry->GetContentState());
+
+ // The navigation could have been issued by the renderer, so be sure that
+ // we update our current index.
+ last_committed_entry_index_ = existing_entry_index;
+ IndexOfActiveEntryChanged();
+ }
+
+ delete entry;
+
+ NotifyNavigationStateChanged();
+}
+
+void NavigationControllerBase::DiscardPendingEntry() {
+ DiscardPendingEntryInternal();
+
+ // Derived classes may do additional things in this case.
+}
+
+int NavigationControllerBase::GetIndexOfEntry(
+ const NavigationEntry* entry) const {
+ NavigationEntryList::const_iterator i = find(entries_.begin(), entries_.end(), entry);
+ if (i == entries_.end())
+ return -1;
+ return static_cast<int>(i - entries_.begin());
+}
+
+void NavigationControllerBase::DiscardPendingEntryInternal() {
+ if (pending_entry_index_ == -1)
+ delete pending_entry_;
+ pending_entry_ = NULL;
+ pending_entry_index_ = -1;
+}
+
+void NavigationControllerBase::InsertEntry(NavigationEntry* entry) {
+ DCHECK(entry->GetTransition() != PageTransition::AUTO_SUBFRAME);
+
+ DiscardPendingEntryInternal();
+
+ int current_size = static_cast<int>(entries_.size());
+
+ // Prune any entry which are in front of the current entry
+ if (current_size > 0) {
+ while (last_committed_entry_index_ < (current_size - 1)) {
+ PruneEntryAtIndex(current_size - 1);
+ delete entries_[current_size - 1];
+ entries_.pop_back();
+ current_size--;
+ }
+ NotifyPrunedEntries();
+ }
+
+ entries_.push_back(entry);
+ last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
+
+ NotifyNavigationStateChanged();
+}
+
+void NavigationControllerBase::ResetInternal() {
+ // WARNING: this is invoked from the destructor, be sure not to invoke any
+ // virtual methods from this.
+ for (int i = 0, c = static_cast<int>(entries_.size()); i < c; ++i)
+ delete entries_[i];
+ entries_.clear();
+
+ DiscardPendingEntryInternal();
+}
+
+#ifndef NDEBUG
+
+void NavigationControllerBase::Dump() {
+ int i,c;
+ for (i = 1, c = static_cast<int>(entries_.size()); i < c; ++i) {
+ DLOG(INFO) << entries_[i]->GetURL().spec();
+ }
+}
+
+#endif
diff --git a/webkit/tools/test_shell/temp/navigation_controller_base.h b/webkit/tools/test_shell/temp/navigation_controller_base.h
new file mode 100644
index 0000000..d4b3785
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_controller_base.h
@@ -0,0 +1,220 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
+
+#include <vector>
+
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+
+class NavigationEntry;
+
+typedef int TabContentsType;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NavigationControllerBase class
+//
+// A NavigationControllerBase maintains navigation data (like session history).
+//
+////////////////////////////////////////////////////////////////////////////////
+class NavigationControllerBase {
+ public:
+ NavigationControllerBase();
+ virtual ~NavigationControllerBase();
+
+ // Empties the history list.
+ virtual void Reset();
+
+ // Returns the active entry, which is the pending entry if a navigation is in
+ // progress or the last committed entry otherwise. NOTE: This can be NULL!!
+ //
+ // If you are trying to get the current state of the NavigationControllerBase,
+ // this is the method you will typically want to call.
+ //
+ NavigationEntry* GetActiveEntry() const;
+
+ // Returns the index from which we would go back/forward or reload. This is
+ // the last_committed_entry_index_ if pending_entry_index_ is -1. Otherwise,
+ // it is the pending_entry_index_.
+ int GetCurrentEntryIndex() const;
+
+ // Returns the pending entry corresponding to the navigation that is
+ // currently in progress, or null if there is none.
+ NavigationEntry* GetPendingEntry() const {
+ return pending_entry_;
+ }
+
+ // Returns the index of the pending entry or -1 if the pending entry
+ // corresponds to a new navigation (created via LoadURL).
+ int GetPendingEntryIndex() const {
+ return pending_entry_index_;
+ }
+
+ // Returns the last committed entry, which may be null if there are no
+ // committed entries.
+ NavigationEntry* GetLastCommittedEntry() const;
+
+ // Returns the index of the last committed entry.
+ int GetLastCommittedEntryIndex() const {
+ return last_committed_entry_index_;
+ }
+
+ // Returns the number of entries in the NavigationControllerBase, excluding
+ // the pending entry if there is one.
+ int GetEntryCount() const {
+ return static_cast<int>(entries_.size());
+ }
+
+ NavigationEntry* GetEntryAtIndex(int index) const {
+ return entries_.at(index);
+ }
+
+ // Returns the entry at the specified offset from current. Returns NULL
+ // if out of bounds.
+ NavigationEntry* GetEntryAtOffset(int offset) const;
+
+ bool CanStop() const;
+
+ // Return whether this controller can go back.
+ bool CanGoBack() const;
+
+ // Return whether this controller can go forward.
+ bool CanGoForward() const;
+
+ // Causes the controller to go back.
+ void GoBack();
+
+ // Causes the controller to go forward.
+ void GoForward();
+
+ // Causes the controller to go to the specified index.
+ void GoToIndex(int index);
+
+ // Causes the controller to go to the specified offset from current. Does
+ // nothing if out of bounds.
+ void GoToOffset(int offset);
+
+ // Causes the controller to stop a pending navigation if any.
+ void Stop();
+
+ // Causes the controller to reload the current (or pending) entry.
+ void Reload();
+
+ // Causes the controller to load the specified entry. The controller
+ // assumes ownership of the entry.
+ // NOTE: Do not pass an entry that the controller already owns!
+ void LoadEntry(NavigationEntry* entry);
+
+ // Return the entry with the corresponding type and page_id, or NULL if
+ // not found.
+ NavigationEntry* GetEntryWithPageID(TabContentsType type,
+ int32 page_id) const;
+
+#ifndef NDEBUG
+ void Dump();
+#endif
+
+ // --------------------------------------------------------------------------
+ // For use by NavigationControllerBase clients:
+
+ // Used to inform the NavigationControllerBase of a navigation being committed
+ // for a tab. The controller takes ownership of the entry. Any entry located
+ // forward to the current entry will be deleted. The new entry becomes the
+ // current entry.
+ virtual void DidNavigateToEntry(NavigationEntry* entry);
+
+ // Used to inform the NavigationControllerBase to discard its pending entry.
+ virtual void DiscardPendingEntry();
+
+ // Returns the index of the specified entry, or -1 if entry is not contained
+ // in this NavigationControllerBase.
+ int GetIndexOfEntry(const NavigationEntry* entry) const;
+
+ protected:
+ // Returns the largest page ID seen. When PageIDs come in larger than
+ // this (via DidNavigateToEntry), we know that we've navigated to a new page.
+ virtual int GetMaxPageID() const = 0;
+
+ // Actually issues the navigation held in pending_entry.
+ virtual void NavigateToPendingEntry(bool reload) = 0;
+
+ // Allows the derived class to issue notifications that a load has been
+ // committed.
+ virtual void NotifyNavigationStateChanged() {}
+
+ // Invoked when entries have been pruned, or removed. For example, if the
+ // current entries are [google, digg, yahoo], with the current entry google,
+ // and the user types in cnet, then digg and yahoo are pruned.
+ virtual void NotifyPrunedEntries() {}
+
+ // Invoked when the index of the active entry may have changed.
+ virtual void IndexOfActiveEntryChanged() {}
+
+ // Inserts an entry after the current position, removing all entries after it.
+ // The new entry will become the active one.
+ virtual void InsertEntry(NavigationEntry* entry);
+
+ // Called when navigations cause entries forward of and including the
+ // specified index are pruned.
+ virtual void PruneEntryAtIndex(int prune_index) { }
+
+ // Discards the pending entry without updating active_contents_
+ void DiscardPendingEntryInternal();
+
+ // Return the index of the entry with the corresponding type and page_id,
+ // or -1 if not found.
+ int GetEntryIndexWithPageID(TabContentsType type, int32 page_id) const;
+
+ // List of NavigationEntry for this tab
+ typedef std::vector<NavigationEntry*> NavigationEntryList;
+ typedef NavigationEntryList::iterator NavigationEntryListIterator;
+ NavigationEntryList entries_;
+
+ // An entry we haven't gotten a response for yet. This will be discarded
+ // when we navigate again. It's used only so we know what the currently
+ // displayed tab is.
+ NavigationEntry* pending_entry_;
+
+ // currently visible entry
+ int last_committed_entry_index_;
+
+ // index of pending entry if it is in entries_, or -1 if pending_entry_ is a
+ // new entry (created by LoadURL).
+ int pending_entry_index_;
+
+ private:
+ // Implementation of Reset and the destructor. Deletes entries
+ void ResetInternal();
+
+ DISALLOW_EVIL_CONSTRUCTORS(NavigationControllerBase);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
diff --git a/webkit/tools/test_shell/temp/navigation_entry.h b/webkit/tools/test_shell/temp/navigation_entry.h
new file mode 100644
index 0000000..c40eb5b
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_entry.h
@@ -0,0 +1,154 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+#include "googleurl/src/gurl.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NavigationEntry class
+//
+// A NavigationEntry is a data structure that captures all the information
+// required to recreate a browsing state. This includes some opaque binary
+// state as provided by the TabContents as well as some clear text title and
+// uri which is used for our user interface.
+//
+////////////////////////////////////////////////////////////////////////////////
+class NavigationEntry {
+ public:
+ // Create a new NavigationEntry.
+ explicit NavigationEntry(TabContentsType type)
+ : type_(type),
+ page_id_(-1),
+ transition_(PageTransition::LINK) {
+ }
+
+ NavigationEntry(TabContentsType type,
+ int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition)
+ : type_(type),
+ page_id_(page_id),
+ url_(url),
+ title_(title),
+ transition_(transition) {
+ }
+
+ // virtual to allow test_shell to extend the class.
+ virtual ~NavigationEntry() {
+ }
+
+ // Return the TabContents type required to display this entry. Immutable
+ // because a tab can never change its type.
+ TabContentsType GetType() const { return type_; }
+
+ // Set / Get the URI
+ void SetURL(const GURL& url) { url_ = url; }
+ const GURL& GetURL() const { return url_; }
+
+ void SetDisplayURL(const GURL& url) {
+ if (url == url_) {
+ display_url_ = GURL::EmptyGURL();
+ } else {
+ display_url_ = url;
+ }
+ }
+
+ bool HasDisplayURL() { return !display_url_.is_empty(); }
+
+ const GURL& GetDisplayURL() const {
+ if (display_url_.is_empty()) {
+ return url_;
+ } else {
+ return display_url_;
+ }
+ }
+
+ // Set / Get the title
+ void SetTitle(const std::wstring& a_title) { title_ = a_title; }
+ const std::wstring& GetTitle() const { return title_; }
+
+ // Set / Get opaque state.
+ // WARNING: This state is saved to the database and used to restore previous
+ // states. If you use write a custom TabContents and provide your own
+ // state make sure you have the ability to modify the format in the future
+ // while being able to deal with older versions.
+ void SetContentState(const std::string& state) { state_ = state; }
+ const std::string& GetContentState() const { return state_; }
+
+ // Get the page id corresponding to the tab's state.
+ void SetPageID(int page_id) { page_id_ = page_id; }
+ int32 GetPageID() const { return page_id_; }
+
+ // The transition type indicates what the user did to move to this page from
+ // the previous page.
+ void SetTransition(PageTransition::Type transition) {
+ transition_ = transition;
+ }
+ PageTransition::Type GetTransition() const { return transition_; }
+
+ // Set / Get favicon URL.
+ void SetFavIconURL(const GURL& url) { fav_icon_url_ = url; }
+ const GURL& GetFavIconURL() const { return fav_icon_url_; }
+
+ // This is the URL the user typed in. This may be invalid.
+ void SetUserTypedURL(const GURL& url) { user_typed_url_ = url; }
+ const GURL& GetUserTypedURL() const { return user_typed_url_; }
+
+ // If the user typed url is valid it is returned, otherwise url is returned.
+ const GURL& GetUserTypedURLOrURL() const {
+ return user_typed_url_.is_valid() ? user_typed_url_ : url_;
+ }
+
+ private:
+ TabContentsType type_;
+
+ // Describes the current page that the tab represents. This is not relevant
+ // for all tab contents types.
+ int32 page_id_;
+
+ GURL url_;
+ // The URL the user typed in.
+ GURL user_typed_url_;
+ std::wstring title_;
+ GURL fav_icon_url_;
+ GURL display_url_;
+
+ std::string state_;
+
+ PageTransition::Type transition_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
diff --git a/webkit/tools/test_shell/temp/page_transition_types.h b/webkit/tools/test_shell/temp/page_transition_types.h
new file mode 100644
index 0000000..1253963
--- /dev/null
+++ b/webkit/tools/test_shell/temp/page_transition_types.h
@@ -0,0 +1,173 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// This class is for scoping only.
+class PageTransition {
+ public:
+ // Types of transitions between pages. These are stored in the history
+ // database to separate visits, and are reported by the renderer for page
+ // navigations.
+ //
+ // WARNING: don't change these numbers, they are written directly into the
+ // history database, so future versions will need the same values to match
+ // the enums.
+ //
+ // A type is made of a core value and a set of qualifiers. A type has one
+ // core value and 0 or or more qualifiers.
+ enum Type {
+ // User got to this page by clicking a link on another page.
+ LINK = 0,
+
+ // User got this page by typing the URL in the URL bar. This should not be
+ // used for cases where the user selected a choice that didn't look at all
+ // like a URL; see GENERATED below.
+ //
+ // We also use this for other "explicit" navigation actions such as command
+ // line arguments.
+ TYPED = 1,
+
+ // User got to this page through a suggestion in the UI, for example,
+ // through the destinations page.
+ AUTO_BOOKMARK = 2,
+
+ // This is a subframe navigation. This is any content that is automatically
+ // loaded in a non-toplevel frame. For example, if a page consists of
+ // several frames containing ads, those ad URLs will have this transition
+ // type. The user may not even realize the content in these pages is a
+ // separate frame, so may not care about the URL (see MANUAL below).
+ AUTO_SUBFRAME = 3,
+
+ // For subframe navigations that are explicitly requested by the user and
+ // generate new navigation entries in the back/forward list. These are
+ // probably more important than frames that were automatically loaded in
+ // the background because the user probably cares about the fact that this
+ // link was loaded.
+ MANUAL_SUBFRAME = 4,
+
+ // User got to this page by typing in the URL bar and selecting an entry
+ // that did not look like a URL. For example, a match might have the URL
+ // of a Google search result page, but appear like "Search Google for ...".
+ // These are not quite the same as TYPED navigations because the user
+ // didn't type or see the destination URL.
+ GENERATED = 5,
+
+ // The page was specified in the command line or is the start page.
+ START_PAGE = 6,
+
+ // The user filled out values in a form and submitted it. NOTE that in
+ // some situations submitting a form does not result in this transition
+ // type. This can happen if the form uses script to submit the contents.
+ FORM_SUBMIT = 7,
+
+ // The user "reloaded" the page, either by hitting the reload button or by
+ // hitting enter in the address bar. NOTE: This is distinct from the
+ // concept of whether a particular load uses "reload semantics" (i.e.
+ // bypasses cached data). For this reason, lots of code needs to pass
+ // around the concept of whether a load should be treated as a "reload"
+ // separately from their tracking of this transition type, which is mainly
+ // used for proper scoring for consumers who care about how frequently a
+ // user typed/visited a particular URL.
+ RELOAD = 8,
+
+ // ADDING NEW CORE VALUE? Be sure to update the LAST_CORE and CORE_MASK
+ // values below.
+ LAST_CORE = RELOAD,
+ CORE_MASK = 0xFF,
+
+ // Qualifiers
+ // Any of the core values above can be augmented by one or more qualifiers.
+ // These qualifiers further define the transition
+
+ // The beginning of a navigation chain.
+ CHAIN_START = 0x10000000,
+
+ // The last transition in a redirect chain.
+ CHAIN_END = 0x20000000,
+
+ // Redirects caused by JavaScript or a meta refresh tag on the page.
+ CLIENT_REDIRECT = 0x40000000,
+
+ // Redirects sent from the server by HTTP headers. It might be nice to
+ // break this out into 2 types in the future: permanent or temporary if we
+ // can get that information from WebKit.
+ SERVER_REDIRECT = 0x80000000,
+
+ // Used to test whether a transition involves a redirect
+ IS_REDIRECT_MASK = 0xC0000000,
+
+ // General mask defining the bits used for the qualifiers
+ QUALIFIER_MASK = 0xFFFFFF00
+ };
+
+ static bool ValidType(int32 type) {
+ int32 t = StripQualifier(static_cast<Type>(type));
+ return (t >= 0 && t <= LAST_CORE &&
+ (type & ~(QUALIFIER_MASK | CORE_MASK)) == 0);
+ }
+
+ static Type FromInt(int32 type) {
+ if (!ValidType(type)) {
+ NOTREACHED() << "Invalid transition type " << type;
+
+ // Return a safe default so we don't have corrupt data in release mode.
+ return LINK;
+ }
+ return static_cast<Type>(type);
+ }
+
+ // Returns true if the given transition is a top-level frame transition, or
+ // false if the transition was for a subframe.
+ static bool IsMainFrame(Type type) {
+ int32 t = StripQualifier(type);
+ return (t != AUTO_SUBFRAME && t != MANUAL_SUBFRAME);
+ }
+
+ // Returns whether a transition involves a redirection
+ static bool IsRedirect(Type type) {
+ return (type & IS_REDIRECT_MASK) != 0;
+ }
+
+ // Simplifies the provided transition by removing any qualifier
+ static Type StripQualifier(Type type) {
+ return static_cast<Type>(type & ~QUALIFIER_MASK);
+ }
+
+ // Return the qualifier
+ static int32 GetQualifier(Type type) {
+ return type & QUALIFIER_MASK;
+ }
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
diff --git a/webkit/tools/test_shell/test_navigation_controller.cc b/webkit/tools/test_shell/test_navigation_controller.cc
new file mode 100644
index 0000000..8262967
--- /dev/null
+++ b/webkit/tools/test_shell/test_navigation_controller.cc
@@ -0,0 +1,107 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+
+#include "base/logging.h"
+#include "webkit/glue/webhistoryitem.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+// ----------------------------------------------------------------------------
+// TestNavigationEntry
+
+TestNavigationEntry::TestNavigationEntry()
+ : NavigationEntry(GetTabContentsType()) {
+}
+
+TestNavigationEntry::TestNavigationEntry(int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition,
+ const std::wstring& target_frame)
+ : NavigationEntry(GetTabContentsType(), page_id, url, title, transition),
+ target_frame_(target_frame) {
+}
+
+TestNavigationEntry::~TestNavigationEntry() {
+}
+
+void TestNavigationEntry::SetContentState(const std::string& state) {
+ cached_history_item_ = NULL; // invalidate our cached item
+ NavigationEntry::SetContentState(state);
+}
+
+WebHistoryItem* TestNavigationEntry::GetHistoryItem() const {
+ if (!cached_history_item_) {
+ TestShellExtraRequestData* extra_data =
+ new TestShellExtraRequestData(GetPageID(), GetTransition());
+ cached_history_item_ =
+ WebHistoryItem::Create(GetURL(), GetTitle(), GetContentState(),
+ extra_data);
+ }
+ return cached_history_item_;
+}
+
+// ----------------------------------------------------------------------------
+// TestNavigationController
+
+TestNavigationController::TestNavigationController(TestShell* shell)
+ : shell_(shell),
+ max_page_id_(-1) {
+}
+
+TestNavigationController::~TestNavigationController() {
+}
+
+void TestNavigationController::Reset() {
+ NavigationControllerBase::Reset();
+ max_page_id_ = -1;
+}
+
+void TestNavigationController::NavigateToPendingEntry(bool reload) {
+ // For session history navigations only the pending_entry_index_ is set.
+ if (!pending_entry_) {
+ DCHECK(pending_entry_index_ != -1);
+ pending_entry_ = entries_[pending_entry_index_];
+ }
+
+ if (shell_->Navigate(*pending_entry_, reload)) {
+ // Note: this is redundant if navigation completed synchronously because
+ // DidNavigateToEntry call this as well.
+ NotifyNavigationStateChanged();
+ } else {
+ DiscardPendingEntry();
+ }
+}
+
+void TestNavigationController::NotifyNavigationStateChanged() {
+ NavigationEntry* entry = GetActiveEntry();
+ if (entry)
+ max_page_id_ = std::max(max_page_id_, entry->GetPageID());
+}
diff --git a/webkit/tools/test_shell/test_navigation_controller.h b/webkit/tools/test_shell/test_navigation_controller.h
new file mode 100644
index 0000000..4bdcad8
--- /dev/null
+++ b/webkit/tools/test_shell/test_navigation_controller.h
@@ -0,0 +1,118 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
+
+#include <vector>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/tools/test_shell/temp/navigation_controller_base.h"
+#include "webkit/tools/test_shell/temp/navigation_entry.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+
+class GURL;
+class TestShell;
+class WebHistoryItem;
+
+// Associated with browser-initated navigations to hold tracking data.
+class TestShellExtraRequestData : public WebRequest::ExtraData {
+ public:
+ TestShellExtraRequestData(int32 pending_page_id,
+ PageTransition::Type transition)
+ : WebRequest::ExtraData(),
+ pending_page_id(pending_page_id),
+ transition_type(transition),
+ request_committed(false) {
+ }
+
+ // Contains the page_id for this navigation or -1 if there is none yet.
+ int32 pending_page_id;
+
+ // Contains the transition type that the browser specified when it
+ // initiated the load.
+ PageTransition::Type transition_type;
+
+ // True if we have already processed the "DidCommitLoad" event for this
+ // request. Used by session history.
+ bool request_committed;
+};
+
+// Same as TestNavigationEntry, but caches the HistoryItem.
+class TestNavigationEntry : public NavigationEntry {
+ public:
+ TestNavigationEntry();
+ TestNavigationEntry(int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition,
+ const std::wstring& target_frame);
+
+ ~TestNavigationEntry();
+
+ // We don't care about tab contents types, so just pick one and use it
+ // everywhere.
+ static TabContentsType GetTabContentsType() {
+ return 0;
+ }
+
+ void SetContentState(const std::string& state);
+ WebHistoryItem* GetHistoryItem() const;
+
+ const std::wstring& GetTargetFrame() const { return target_frame_; }
+
+private:
+ mutable scoped_refptr<WebHistoryItem> cached_history_item_;
+ std::wstring target_frame_;
+};
+
+// Test shell's NavigationController. The goal is to be as close to the Chrome
+// version as possible.
+class TestNavigationController : public NavigationControllerBase {
+ public:
+ TestNavigationController(TestShell* shell);
+ ~TestNavigationController();
+
+ virtual void Reset();
+
+ private:
+ virtual int GetMaxPageID() const { return max_page_id_; }
+ virtual void NavigateToPendingEntry(bool reload);
+ virtual void NotifyNavigationStateChanged();
+
+ TestShell* shell_;
+ int max_page_id_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestNavigationController);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/test_shell.cc b/webkit/tools/test_shell/test_shell.cc
new file mode 100644
index 0000000..407b95d
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.cc
@@ -0,0 +1,1128 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <atlbase.h>
+#include <commdlg.h>
+#include <objbase.h>
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include "webkit/tools/test_shell/test_shell.h"
+
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#include "base/file_util.h"
+#include "base/gfx/bitmap_platform_device.h"
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/size.h"
+#include "base/icu_util.h"
+#include "base/md5.h"
+#include "base/memory_debug.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/stats_table.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "googleurl/src/url_util.h"
+#include "net/base/mime_util.h"
+#include "net/url_request/url_request_file_job.h"
+#include "net/url_request/url_request_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/webdatasource.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/webkit_resources.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/glue/webview.h"
+#include "webkit/glue/webwidget.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+
+#include "webkit_strings.h"
+
+#include "SkBitmap.h"
+
+using std::min;
+using std::max;
+
+#define MAX_LOADSTRING 100
+
+#define BUTTON_WIDTH 72
+#define URLBAR_HEIGHT 24
+
+// Global Variables:
+static TCHAR g_windowTitle[MAX_LOADSTRING]; // The title bar text
+static TCHAR g_windowClass[MAX_LOADSTRING]; // The main window class name
+
+// Forward declarations of functions included in this code module:
+static INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+
+// Default timeout for page load when running non-interactive file
+// tests, in ms.
+const int kDefaultFileTestTimeoutMillisecs = 10 * 1000;
+
+// Content area size for newly created windows.
+const int kTestWindowWidth = 800;
+const int kTestWindowHeight = 600;
+
+// The W3C SVG layout tests use a different size than the other layout tests
+const int kSVGTestWindowWidth = 480;
+const int kSVGTestWindowHeight = 360;
+
+// Hide the window offscreen when it is non-interactive.
+// This would correspond with a minimized window position if x = y = -32000.
+// However we shift the x to 0 to pass test cross-frame-access-put.html
+// which expects screenX/screenLeft to be 0 (http://b/issue?id=1227945).
+// TODO(ericroman): x should be defined as 0 rather than -4. There is
+// probably a frameborder not being accounted for in the setting/getting.
+const int kTestWindowXLocation = -4;
+const int kTestWindowYLocation = -32000;
+
+// Initialize static member variable
+WindowList* TestShell::window_list_;
+HINSTANCE TestShell::instance_handle_;
+WebPreferences* TestShell::web_prefs_ = NULL;
+bool TestShell::interactive_ = true;
+int TestShell::file_test_timeout_ms_ = kDefaultFileTestTimeoutMillisecs;
+
+// URLRequestTestShellFileJob is used to serve the inspector
+class URLRequestTestShellFileJob : public URLRequestFileJob {
+ public:
+ virtual ~URLRequestTestShellFileJob() { }
+
+ static URLRequestJob* InspectorFactory(URLRequest* request,
+ const std::string& scheme) {
+ std::wstring path;
+ PathService::Get(base::DIR_EXE, &path);
+ file_util::AppendToPath(&path, L"Resources");
+ file_util::AppendToPath(&path, L"Inspector");
+ file_util::AppendToPath(&path, UTF8ToWide(request->url().path()));
+ return new URLRequestTestShellFileJob(request, path);
+ }
+
+ private:
+ URLRequestTestShellFileJob(URLRequest* request, const std::wstring& path)
+ : URLRequestFileJob(request) {
+ this->file_path_ = path; // set URLRequestFileJob::file_path_
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(URLRequestTestShellFileJob);
+};
+
+TestShell::TestShell()
+ : m_mainWnd(NULL),
+ m_editWnd(NULL),
+ m_webViewHost(NULL),
+ m_popupHost(NULL),
+ m_focusedWidgetHost(NULL),
+ default_edit_wnd_proc_(0),
+ delegate_(new TestWebViewDelegate(this)),
+ test_is_preparing_(false),
+ test_is_pending_(false),
+ is_modal_(false),
+ dump_stats_table_on_exit_(false) {
+ layout_test_controller_.reset(new LayoutTestController(this));
+ event_sending_controller_.reset(new EventSendingController(this));
+ text_input_controller_.reset(new TextInputController(this));
+ navigation_controller_.reset(new TestNavigationController(this));
+
+ URLRequestFilter* filter = URLRequestFilter::GetInstance();
+ filter->AddHostnameHandler("test-shell-resource", "inspector",
+ &URLRequestTestShellFileJob::InspectorFactory);
+ url_util::AddStandardScheme("test-shell-resource");
+}
+
+TestShell::~TestShell() {
+
+ // Call GC twice to clean up garbage.
+ CallJSGC();
+ CallJSGC();
+
+ // When the window is destroyed, tell the Edit field to forget about us,
+ // otherwise we will crash.
+ win_util::SetWindowProc(m_editWnd, default_edit_wnd_proc_);
+ win_util::SetWindowUserData(m_editWnd, NULL);
+
+ StatsTable *table = StatsTable::current();
+ if (dump_stats_table_on_exit_) {
+ // Dump the stats table.
+ printf("<stats>\n");
+ if (table != NULL) {
+ int counter_max = table->GetMaxCounters();
+ for (int index=0; index < counter_max; index++) {
+ std::wstring name(table->GetRowName(index));
+ if (name.length() > 0) {
+ int value = table->GetRowValue(index);
+ printf("%s:\t%d\n", WideToUTF8(name).c_str(), value);
+ }
+ }
+ }
+ printf("</stats>\n");
+ }
+}
+
+// All fatal log messages (e.g. DCHECK failures) imply unit test failures
+static void UnitTestAssertHandler(const std::string& str) {
+ FAIL() << str;
+}
+
+// static
+void TestShell::InitLogging(bool suppress_error_dialogs) {
+ if (!IsDebuggerPresent() && suppress_error_dialogs) {
+ UINT new_flags = SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX |
+ SEM_NOOPENFILEERRORBOX;
+ // Preserve existing error mode, as discussed at http://t/dmea
+ UINT existing_flags = SetErrorMode(new_flags);
+ SetErrorMode(existing_flags | new_flags);
+
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+ }
+
+ // We might have multiple test_shell processes going at once
+ std::wstring log_filename;
+ PathService::Get(base::DIR_EXE, &log_filename);
+ file_util::AppendToPath(&log_filename, L"test_shell.log");
+ logging::InitLogging(log_filename.c_str(),
+ logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+
+ // we want process and thread IDs because we may have multiple processes
+ logging::SetLogItems(true, true, false, true);
+}
+
+// static
+void TestShell::CleanupLogging() {
+ logging::CloseLogFile();
+}
+
+// static
+void TestShell::InitializeTestShell(bool interactive) {
+ // Start COM stuff.
+ HRESULT res = OleInitialize(NULL);
+ DCHECK(SUCCEEDED(res));
+
+ window_list_ = new WindowList;
+ instance_handle_ = ::GetModuleHandle(NULL);
+ interactive_ = interactive;
+
+ web_prefs_ = new WebPreferences;
+
+ ResetWebPreferences();
+}
+
+// static
+void TestShell::ResetWebPreferences() {
+ DCHECK(web_prefs_);
+
+ // Match the settings used by Mac DumpRenderTree.
+ if (web_prefs_) {
+ *web_prefs_ = WebPreferences();
+ web_prefs_->standard_font_family = L"Times";
+ web_prefs_->fixed_font_family = L"Courier";
+ web_prefs_->serif_font_family = L"Times";
+ web_prefs_->sans_serif_font_family = L"Helvetica";
+ web_prefs_->cursive_font_family = L"Apple Chancery";
+ web_prefs_->fantasy_font_family = L"Papyrus";
+ web_prefs_->default_encoding = L"ISO-8859-1";
+ web_prefs_->default_font_size = 16;
+ web_prefs_->default_fixed_font_size = 13;
+ web_prefs_->minimum_font_size = 1;
+ web_prefs_->minimum_logical_font_size = 9;
+ web_prefs_->javascript_can_open_windows_automatically = true;
+ web_prefs_->dom_paste_enabled = true;
+ web_prefs_->developer_extras_enabled = interactive_;
+ web_prefs_->shrinks_standalone_images_to_fit = false;
+ web_prefs_->uses_universal_detector = false;
+ web_prefs_->text_areas_are_resizable = false;
+ web_prefs_->user_agent = webkit_glue::GetDefaultUserAgent();
+ web_prefs_->dashboard_compatibility_mode = false;
+ web_prefs_->java_enabled = true;
+ }
+}
+
+// static
+void TestShell::ShutdownTestShell() {
+ delete window_list_;
+ SimpleResourceLoaderBridge::Shutdown();
+ delete TestShell::web_prefs_;
+ OleUninitialize();
+}
+
+bool TestShell::Initialize(const std::wstring& startingURL) {
+ // Perform application initialization:
+ m_mainWnd = CreateWindow(g_windowClass, g_windowTitle,
+ WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+ NULL, NULL, instance_handle_, NULL);
+ win_util::SetWindowUserData(m_mainWnd, this);
+
+ HWND hwnd;
+ int x = 0;
+
+ hwnd = CreateWindow(L"BUTTON", L"Back",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_BACK, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Forward",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_FORWARD, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Reload",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_RELOAD, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Stop",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_STOP, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ // this control is positioned by ResizeSubViews
+ m_editWnd = CreateWindow(L"EDIT", 0,
+ WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL,
+ x, 0, 0, 0, m_mainWnd, 0, instance_handle_, 0);
+
+ default_edit_wnd_proc_ =
+ win_util::SetWindowProc(m_editWnd, TestShell::EditWndProc);
+ win_util::SetWindowUserData(m_editWnd, this);
+
+ // create webview
+ m_webViewHost.reset(
+ WebViewHost::Create(m_mainWnd, delegate_.get(), *TestShell::web_prefs_));
+ webView()->SetUseEditorDelegate(true);
+ delegate_->RegisterDragDrop();
+
+ // Load our initial content.
+ if (!startingURL.empty())
+ LoadURL(startingURL.c_str());
+
+ ShowWindow(webViewWnd(), SW_SHOW);
+
+ bool bIsSVGTest = startingURL.find(L"W3C-SVG-1.1") != std::wstring::npos;
+
+ if (bIsSVGTest) {
+ SizeTo(kSVGTestWindowWidth, kSVGTestWindowHeight);
+ } else {
+ SizeToDefault();
+ }
+
+ return true;
+}
+
+void TestShell::TestFinished() {
+ if (!test_is_pending_)
+ return; // reached when running under test_shell_tests
+
+ UINT_PTR timer_id = reinterpret_cast<UINT_PTR>(this);
+ KillTimer(mainWnd(), timer_id);
+
+ test_is_pending_ = false;
+ MessageLoop::current()->Quit();
+}
+
+// Thread main to run for the thread which just tests for timeout.
+unsigned int __stdcall WatchDogThread(void *arg)
+{
+ // If we're debugging a layout test, don't timeout.
+ if (::IsDebuggerPresent())
+ return 0;
+
+ TestShell* shell = static_cast<TestShell*>(arg);
+ DWORD timeout = static_cast<DWORD>(shell->GetFileTestTimeout() * 2.5);
+ DWORD rv = WaitForSingleObject(shell->finished_event(), timeout);
+ if (rv == WAIT_TIMEOUT) {
+ // Print a warning to be caught by the layout-test script.
+ // Note: the layout test driver may or may not recognize
+ // this as a timeout.
+ puts("#TEST_TIMED_OUT\n");
+ puts("#EOF\n");
+ fflush(stdout);
+ TerminateProcess(GetCurrentProcess(), 0);
+ }
+ // Finished normally.
+ return 0;
+}
+
+void TestShell::WaitTestFinished() {
+ DCHECK(!test_is_pending_) << "cannot be used recursively";
+
+ test_is_pending_ = true;
+
+ // Create a watchdog thread which just sets a timer and
+ // kills the process if it times out. This catches really
+ // bad hangs where the shell isn't coming back to the
+ // message loop. If the watchdog is what catches a
+ // timeout, it can't do anything except terminate the test
+ // shell, which is unfortunate.
+ finished_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DCHECK(finished_event_ != NULL);
+
+ HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(
+ NULL,
+ 0,
+ &WatchDogThread,
+ this,
+ 0,
+ 0));
+ DCHECK(thread_handle != NULL);
+
+ // TestFinished() will post a quit message to break this loop when the page
+ // finishes loading.
+ while (test_is_pending_)
+ MessageLoop::current()->Run();
+
+ // Tell the watchdog that we are finished.
+ SetEvent(finished_event_);
+
+ // Wait to join the watchdog thread. (up to 1s, then quit)
+ WaitForSingleObject(thread_handle, 1000);
+}
+
+void TestShell::Show(WebView* webview, WindowOpenDisposition disposition) {
+ delegate_->Show(webview, disposition);
+}
+
+void TestShell::SetFocus(WebWidgetHost* host, bool enable) {
+ if (interactive_) {
+ if (enable) {
+ ::SetFocus(host->window_handle());
+ } else {
+ if (GetFocus() == host->window_handle())
+ ::SetFocus(NULL);
+ }
+ } else {
+ if (enable) {
+ if (m_focusedWidgetHost != host) {
+ if (m_focusedWidgetHost)
+ m_focusedWidgetHost->webwidget()->SetFocus(false);
+ host->webwidget()->SetFocus(enable);
+ m_focusedWidgetHost = host;
+ }
+ } else {
+ if (m_focusedWidgetHost == host) {
+ host->webwidget()->SetFocus(enable);
+ m_focusedWidgetHost = NULL;
+ }
+ }
+ }
+}
+
+void TestShell::BindJSObjectsToWindow(WebFrame* frame) {
+ // Only bind the test classes if we're running tests.
+ if (!interactive_) {
+ layout_test_controller_->BindToJavascript(frame,
+ L"layoutTestController");
+ event_sending_controller_->BindToJavascript(frame,
+ L"eventSender");
+ text_input_controller_->BindToJavascript(frame,
+ L"textInputController");
+ }
+}
+
+
+void TestShell::CallJSGC() {
+ WebFrame* frame = webView()->GetMainFrame();
+ frame->CallJSGC();
+}
+
+
+/*static*/
+bool TestShell::CreateNewWindow(const std::wstring& startingURL,
+ TestShell** result)
+{
+ TestShell* shell = new TestShell();
+ bool rv = shell->Initialize(startingURL);
+ if (rv) {
+ if (result)
+ *result = shell;
+ TestShell::windowList()->push_back(shell->m_mainWnd);
+ }
+ return rv;
+}
+
+WebView* TestShell::CreateWebView(WebView* webview) {
+ // If we're running layout tests, only open a new window if the test has
+ // called layoutTestController.setCanOpenWindows()
+ if (!interactive_ && !layout_test_controller_->CanOpenWindows())
+ return NULL;
+
+ TestShell* new_win;
+ if (!CreateNewWindow(std::wstring(), &new_win))
+ return NULL;
+
+ return new_win->webView();
+}
+
+WebWidget* TestShell::CreatePopupWidget(WebView* webview) {
+ DCHECK(!m_popupHost);
+ m_popupHost = WebWidgetHost::Create(NULL, delegate_.get());
+ ShowWindow(popupWnd(), SW_SHOW);
+
+ return m_popupHost->webwidget();
+}
+
+void TestShell::ClosePopup() {
+ PostMessage(popupWnd(), WM_CLOSE, 0, 0);
+ m_popupHost = NULL;
+}
+
+void TestShell::SizeToDefault() {
+ SizeTo(kTestWindowWidth, kTestWindowHeight);
+}
+
+void TestShell::SizeTo(int width, int height) {
+ RECT rc, rw;
+ GetClientRect(m_mainWnd, &rc);
+ GetWindowRect(m_mainWnd, &rw);
+
+ int client_width = rc.right - rc.left;
+ int window_width = rw.right - rw.left;
+ window_width = (window_width - client_width) + width;
+
+ int client_height = rc.bottom - rc.top;
+ int window_height = rw.bottom - rw.top;
+ window_height = (window_height - client_height) + height;
+
+ // add space for the url bar:
+ window_height += URLBAR_HEIGHT;
+
+ SetWindowPos(m_mainWnd, NULL, 0, 0, window_width, window_height,
+ SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void TestShell::ResizeSubViews() {
+ RECT rc;
+ GetClientRect(m_mainWnd, &rc);
+
+ int x = BUTTON_WIDTH * 4;
+ MoveWindow(m_editWnd, x, 0, rc.right - x, URLBAR_HEIGHT, TRUE);
+
+ MoveWindow(webViewWnd(), 0, URLBAR_HEIGHT, rc.right,
+ rc.bottom - URLBAR_HEIGHT, TRUE);
+}
+
+/* static */ std::string TestShell::DumpImage(
+ WebFrame* web_frame,
+ const std::wstring& file_name) {
+ gfx::BitmapPlatformDevice device(web_frame->CaptureImage(true));
+ const SkBitmap& src_bmp = device.accessBitmap(false);
+
+ // Encode image.
+ std::vector<unsigned char> png;
+ SkAutoLockPixels src_bmp_lock(src_bmp);
+ PNGEncoder::Encode(
+ reinterpret_cast<const unsigned char*>(src_bmp.getPixels()),
+ PNGEncoder::FORMAT_BGRA, src_bmp.width(), src_bmp.height(),
+ static_cast<int>(src_bmp.rowBytes()), true, &png);
+
+ // Write to disk.
+ FILE* file = NULL;
+ if (_wfopen_s(&file, file_name.c_str(), L"wb") == 0) {
+ fwrite(&png[0], 1, png.size(), file);
+ fclose(file);
+ }
+
+ // Compute MD5 sum.
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, src_bmp.getPixels(), src_bmp.getSize());
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+ return MD5DigestToBase16(digest);
+}
+
+/* static */ void TestShell::DumpBackForwardList(std::wstring* result) {
+ result->clear();
+ for (WindowList::iterator iter = TestShell::windowList()->begin();
+ iter != TestShell::windowList()->end(); iter++) {
+ HWND hwnd = *iter;
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ webkit_glue::DumpBackForwardList(shell->webView(), NULL, result);
+ }
+}
+
+/* static */ bool TestShell::RunFileTest(const char *filename,
+ const TestParams& params) {
+ // Load the test file into the first available window.
+ if (TestShell::windowList()->empty()) {
+ LOG(ERROR) << "No windows open.";
+ return false;
+ }
+
+ HWND hwnd = *(TestShell::windowList()->begin());
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ shell->ResetTestController();
+
+ // ResetTestController may have closed the window we were holding on to.
+ // Grab the first window again.
+ hwnd = *(TestShell::windowList()->begin());
+ shell = static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ DCHECK(shell);
+
+ // Clear focus between tests.
+ shell->m_focusedWidgetHost = NULL;
+
+ // Make sure the previous load is stopped.
+ shell->webView()->StopLoading();
+ shell->navigation_controller()->Reset();
+
+ // Clean up state between test runs.
+ webkit_glue::ResetBeforeTestRun(shell->webView());
+ ResetWebPreferences();
+ shell->webView()->SetPreferences(*web_prefs_);
+
+ SetWindowPos(shell->m_mainWnd, NULL,
+ kTestWindowXLocation, kTestWindowYLocation, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER);
+ shell->ResizeSubViews();
+
+ if (strstr(filename, "loading/") || strstr(filename, "loading\\"))
+ shell->layout_test_controller()->SetShouldDumpFrameLoadCallbacks(true);
+
+ shell->test_is_preparing_ = true;
+
+ std::wstring wstr = UTF8ToWide(filename);
+ shell->LoadURL(wstr.c_str());
+
+ shell->test_is_preparing_ = false;
+ shell->WaitTestFinished();
+
+ // Echo the url in the output so we know we're not getting out of sync.
+ printf("#URL:%s\n", filename);
+
+ // Dump the requested representation.
+ WebFrame* webFrame = shell->webView()->GetMainFrame();
+ if (webFrame) {
+ bool should_dump_as_text =
+ shell->layout_test_controller_->ShouldDumpAsText();
+ bool dumped_anything = false;
+ if (params.dump_tree) {
+ dumped_anything = true;
+ // Text output: the test page can request different types of output
+ // which we handle here.
+ if (!should_dump_as_text) {
+ // Plain text pages should be dumped as text
+ std::wstring mime_type = webFrame->GetDataSource()->GetResponseMimeType();
+ should_dump_as_text = (mime_type == L"text/plain");
+ }
+ if (should_dump_as_text) {
+ bool recursive = shell->layout_test_controller_->
+ ShouldDumpChildFramesAsText();
+ std::string data_utf8 = WideToUTF8(
+ webkit_glue::DumpFramesAsText(webFrame, recursive));
+ fwrite(data_utf8.c_str(), 1, data_utf8.size(), stdout);
+ } else {
+ printf("%s", WideToUTF8(
+ webkit_glue::DumpRenderer(webFrame)).c_str());
+
+ bool recursive = shell->layout_test_controller_->
+ ShouldDumpChildFrameScrollPositions();
+ printf("%s", WideToUTF8(
+ webkit_glue::DumpFrameScrollPosition(webFrame, recursive)).
+ c_str());
+ }
+
+ if (shell->layout_test_controller_->ShouldDumpBackForwardList()) {
+ std::wstring bfDump;
+ DumpBackForwardList(&bfDump);
+ printf("%s", WideToUTF8(bfDump).c_str());
+ }
+ }
+
+ if (params.dump_pixels && !should_dump_as_text) {
+ // Image output: we write the image data to the file given on the
+ // command line (for the dump pixels argument), and the MD5 sum to
+ // stdout.
+ dumped_anything = true;
+ std::string md5sum = DumpImage(webFrame, params.pixel_file_name);
+ printf("#MD5:%s\n", md5sum.c_str());
+ }
+ if (dumped_anything)
+ printf("#EOF\n");
+ fflush(stdout);
+ }
+
+ return true;
+}
+
+
+/* static */
+ATOM TestShell::RegisterWindowClass()
+{
+ LoadString(instance_handle_, IDS_APP_TITLE, g_windowTitle, MAX_LOADSTRING);
+ LoadString(instance_handle_, IDC_TESTSHELL, g_windowClass, MAX_LOADSTRING);
+
+ WNDCLASSEX wcex = {
+ /* cbSize = */ sizeof(WNDCLASSEX),
+ /* style = */ CS_HREDRAW | CS_VREDRAW,
+ /* lpfnWndProc = */ TestShell::WndProc,
+ /* cbClsExtra = */ 0,
+ /* cbWndExtra = */ 0,
+ /* hInstance = */ instance_handle_,
+ /* hIcon = */ LoadIcon(instance_handle_, MAKEINTRESOURCE(IDI_TESTSHELL)),
+ /* hCursor = */ LoadCursor(NULL, IDC_ARROW),
+ /* hbrBackground = */ 0,
+ /* lpszMenuName = */ MAKEINTRESOURCE(IDC_TESTSHELL),
+ /* lpszClassName = */ g_windowClass,
+ /* hIconSm = */ LoadIcon(instance_handle_, MAKEINTRESOURCE(IDI_SMALL)),
+ };
+ return RegisterClassEx(&wcex);
+}
+
+LRESULT CALLBACK TestShell::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ TestShell* shell = static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+
+ switch (message) {
+ case WM_COMMAND:
+ {
+ int wmId = LOWORD(wParam);
+ int wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDM_ABOUT:
+ DialogBox(shell->instance_handle_, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd,
+ About);
+ break;
+ case IDM_EXIT:
+ DestroyWindow(hwnd);
+ break;
+ case IDC_NAV_BACK:
+ shell->GoBackOrForward(-1);
+ break;
+ case IDC_NAV_FORWARD:
+ shell->GoBackOrForward(1);
+ break;
+ case IDC_NAV_RELOAD:
+ case IDC_NAV_STOP:
+ {
+ if (wmId == IDC_NAV_RELOAD) {
+ shell->Reload();
+ } else {
+ shell->webView()->StopLoading();
+ }
+ }
+ break;
+ case IDM_DUMP_BODY_TEXT:
+ shell->DumpDocumentText();
+ break;
+ case IDM_DUMP_RENDER_TREE:
+ shell->DumpRenderTree();
+ break;
+ case IDM_SHOW_WEB_INSPECTOR:
+ shell->webView()->InspectElement(0, 0);
+ break;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ // Dump all in use memory just before shutdown if in use memory
+ // debugging has been enabled.
+ base::MemoryDebug::DumpAllMemoryInUse();
+
+ WindowList::iterator entry =
+ std::find(TestShell::windowList()->begin(),
+ TestShell::windowList()->end(), hwnd);
+ if (entry != TestShell::windowList()->end())
+ TestShell::windowList()->erase(entry);
+
+ if (TestShell::windowList()->empty() || shell->is_modal())
+ MessageLoop::current()->Quit();
+ delete shell;
+ }
+ return 0;
+
+ case WM_SIZE:
+ if (shell->webView())
+ shell->ResizeSubViews();
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+#define MAX_URL_LENGTH 1024
+
+LRESULT CALLBACK TestShell::EditWndProc(HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+
+ switch (message) {
+ case WM_CHAR:
+ if (wParam == 13) { // Enter Key
+ wchar_t strPtr[MAX_URL_LENGTH];
+ *((LPWORD)strPtr) = MAX_URL_LENGTH;
+ LRESULT strLen = SendMessage(hwnd, EM_GETLINE, 0, (LPARAM)strPtr);
+ if (strLen > 0)
+ shell->LoadURL(strPtr);
+
+ return 0;
+ }
+ }
+
+ return (LRESULT) CallWindowProc(shell->default_edit_wnd_proc_, hwnd,
+ message, wParam, lParam);
+}
+
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (message) {
+ case WM_INITDIALOG:
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
+
+void TestShell::LoadURL(const wchar_t* url)
+{
+ LoadURLForFrame(url, NULL);
+}
+
+void TestShell::LoadURLForFrame(const wchar_t* url,
+ const wchar_t* frame_name) {
+ if (!url)
+ return;
+
+ bool bIsSVGTest = wcsstr(url, L"W3C-SVG-1.1") > 0;
+
+ if (bIsSVGTest) {
+ SizeTo(kSVGTestWindowWidth, kSVGTestWindowHeight);
+ } else {
+ SizeToDefault();
+ }
+
+ std::wstring urlString(url);
+ if (!urlString.empty() && (PathFileExists(url) || PathIsUNC(url))) {
+ TCHAR fileURL[INTERNET_MAX_URL_LENGTH];
+ DWORD fileURLLength = sizeof(fileURL)/sizeof(fileURL[0]);
+ if (SUCCEEDED(UrlCreateFromPath(url, fileURL, &fileURLLength, 0)))
+ urlString.assign(fileURL);
+ }
+
+ std::wstring frame_string;
+ if (frame_name)
+ frame_string = frame_name;
+
+ navigation_controller_->LoadEntry(new TestNavigationEntry(
+ -1, GURL(urlString), std::wstring(), PageTransition::LINK,
+ frame_string));
+}
+
+bool TestShell::Navigate(const NavigationEntry& entry, bool reload) {
+ const TestNavigationEntry& test_entry =
+ *static_cast<const TestNavigationEntry*>(&entry);
+
+ WebRequestCachePolicy cache_policy;
+ if (reload) {
+ cache_policy = WebRequestReloadIgnoringCacheData;
+ } else if (entry.GetPageID() != -1) {
+ cache_policy = WebRequestReturnCacheDataElseLoad;
+ } else {
+ cache_policy = WebRequestUseProtocolCachePolicy;
+ }
+
+ scoped_ptr<WebRequest> request(WebRequest::Create(entry.GetURL()));
+ request->SetCachePolicy(cache_policy);
+ // If we are reloading, then WebKit will use the state of the current page.
+ // Otherwise, we give it the state to navigate to.
+ if (!reload)
+ request->SetHistoryState(entry.GetContentState());
+
+ request->SetExtraData(
+ new TestShellExtraRequestData(entry.GetPageID(),
+ entry.GetTransition()));
+
+ // Get the right target frame for the entry.
+ WebFrame* frame = webView()->GetMainFrame();
+ if (!test_entry.GetTargetFrame().empty())
+ frame = webView()->GetFrameWithName(test_entry.GetTargetFrame());
+ // TODO(mpcomplete): should we clear the target frame, or should
+ // back/forward navigations maintain the target frame?
+
+ frame->LoadRequest(request.get());
+ SetFocus(webViewHost(), true);
+
+ return true;
+}
+
+void TestShell::GoBackOrForward(int offset) {
+ navigation_controller_->GoToOffset(offset);
+}
+
+bool TestShell::PromptForSaveFile(const wchar_t* prompt_title,
+ std::wstring* result)
+{
+ wchar_t path_buf[MAX_PATH] = L"data.txt";
+
+ OPENFILENAME info = {0};
+ info.lStructSize = sizeof(info);
+ info.hwndOwner = m_mainWnd;
+ info.hInstance = instance_handle_;
+ info.lpstrFilter = L"*.txt";
+ info.lpstrFile = path_buf;
+ info.nMaxFile = arraysize(path_buf);
+ info.lpstrTitle = prompt_title;
+ if (!GetSaveFileName(&info))
+ return false;
+
+ result->assign(info.lpstrFile);
+ return true;
+}
+
+static void WriteTextToFile(const std::wstring& data,
+ const std::wstring& file_path)
+{
+ FILE* fp;
+ errno_t err = _wfopen_s(&fp, file_path.c_str(), L"wt");
+ if (err)
+ return;
+ std::string data_utf8 = WideToUTF8(data);
+ fwrite(data_utf8.c_str(), 1, data_utf8.size(), fp);
+ fclose(fp);
+}
+
+std::wstring TestShell::GetDocumentText()
+{
+ return webkit_glue::DumpDocumentText(webView()->GetMainFrame());
+}
+
+void TestShell::DumpDocumentText()
+{
+ std::wstring file_path;
+ if (!PromptForSaveFile(L"Dump document text", &file_path))
+ return;
+
+ WriteTextToFile(webkit_glue::DumpDocumentText(webView()->GetMainFrame()),
+ file_path);
+}
+
+void TestShell::DumpRenderTree()
+{
+ std::wstring file_path;
+ if (!PromptForSaveFile(L"Dump render tree", &file_path))
+ return;
+
+ WriteTextToFile(webkit_glue::DumpRenderer(webView()->GetMainFrame()),
+ file_path);
+}
+
+void TestShell::Reload() {
+ navigation_controller_->Reload();
+}
+
+/* static */
+std::string TestShell::RewriteLocalUrl(const std::string& url) {
+ // Convert file:///tmp/LayoutTests urls to the actual location on disk.
+ const char kPrefix[] = "file:///tmp/LayoutTests/";
+ const int kPrefixLen = arraysize(kPrefix) - 1;
+
+ std::string new_url(url);
+ if (url.compare(0, kPrefixLen, kPrefix, kPrefixLen) == 0) {
+ std::wstring replace_url;
+ PathService::Get(base::DIR_EXE, &replace_url);
+ file_util::UpOneDirectory(&replace_url);
+ file_util::UpOneDirectory(&replace_url);
+ file_util::AppendToPath(&replace_url, L"webkit");
+ file_util::AppendToPath(&replace_url, L"data");
+ file_util::AppendToPath(&replace_url, L"layout_tests");
+ file_util::AppendToPath(&replace_url, L"LayoutTests");
+ replace_url.push_back(file_util::kPathSeparator);
+ new_url = std::string("file:///") +
+ WideToUTF8(replace_url).append(url.substr(kPrefixLen));
+ }
+ return new_url;
+}
+
+//-----------------------------------------------------------------------------
+
+namespace webkit_glue {
+
+bool HistoryContains(const char16* url, int url_len,
+ const char* document_host, int document_host_len,
+ bool is_dns_prefetch_enabled) {
+ return false;
+}
+
+void DnsPrefetchUrl(const char16* url, int url_length) {}
+
+void PrecacheUrl(const char16* url, int url_length) {}
+
+void AppendToLog(const char* file, int line, const char* msg) {
+ logging::LogMessage(file, line).stream() << msg;
+}
+
+bool GetMimeTypeFromExtension(std::wstring &ext, std::string *mime_type) {
+ return mime_util::GetMimeTypeFromExtension(ext, mime_type);
+}
+
+bool GetMimeTypeFromFile(const std::wstring &file_path,
+ std::string *mime_type) {
+ return mime_util::GetMimeTypeFromFile(file_path, mime_type);
+}
+
+bool GetPreferredExtensionForMimeType(const std::string& mime_type,
+ std::wstring* ext) {
+ return mime_util::GetPreferredExtensionForMimeType(mime_type, ext);
+}
+
+IMLangFontLink2* GetLangFontLink() {
+ return webkit_glue::GetLangFontLinkHelper();
+}
+
+std::wstring GetLocalizedString(int message_id) {
+ const ATLSTRINGRESOURCEIMAGE* image =
+ AtlGetStringResourceImage(_AtlBaseModule.GetModuleInstance(),
+ message_id);
+ if (!image) {
+ NOTREACHED();
+ return L"No string for this identifier!";
+ }
+ return std::wstring(image->achString, image->nLength);
+}
+
+std::string GetDataResource(int resource_id) {
+ if (resource_id == IDR_BROKENIMAGE) {
+ // Use webkit's broken image icon (16x16)
+ static std::string broken_image_data;
+ if (broken_image_data.empty()) {
+ std::wstring path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ file_util::AppendToPath(&path, L"webkit");
+ file_util::AppendToPath(&path, L"tools");
+ file_util::AppendToPath(&path, L"test_shell");
+ file_util::AppendToPath(&path, L"resources");
+ file_util::AppendToPath(&path, L"missingImage.gif");
+ bool success = file_util::ReadFileToString(path, &broken_image_data);
+ if (!success) {
+ LOG(FATAL) << "Failed reading: " << path;
+ }
+ }
+ return broken_image_data;
+ } else if (resource_id == IDR_FEED_PREVIEW) {
+ // It is necessary to return a feed preview template that contains
+ // a {{URL}} substring where the feed URL should go; see the code
+ // that computes feed previews in feed_preview.cc:MakeFeedPreview.
+ // This fixes issue #932714.
+ return std::string("Feed preview for {{URL}}");
+ } else {
+ return std::string();
+ }
+}
+
+HCURSOR LoadCursor(int cursor_id) {
+ return NULL;
+}
+
+bool GetApplicationDirectory(std::wstring *path) {
+ return PathService::Get(base::DIR_EXE, path);
+}
+
+GURL GetInspectorURL() {
+ return GURL("test-shell-resource://inspector/inspector.html");
+}
+
+std::string GetUIResourceProtocol() {
+ return "test-shell-resource";
+}
+
+bool GetExeDirectory(std::wstring *path) {
+ return PathService::Get(base::DIR_EXE, path);
+}
+
+bool SpellCheckWord(const wchar_t* word, int word_len,
+ int* misspelling_start, int* misspelling_len) {
+ // Report all words being correctly spelled.
+ *misspelling_start = 0;
+ *misspelling_len = 0;
+ return true;
+}
+
+bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
+ return NPAPI::PluginList::Singleton()->GetPlugins(refresh, plugins);
+}
+
+bool webkit_glue::IsPluginRunningInRendererProcess() {
+ return true;
+}
+
+bool EnsureFontLoaded(HFONT font) {
+ return true;
+}
+
+MONITORINFOEX GetMonitorInfoForWindow(HWND window) {
+ return webkit_glue::GetMonitorInfoForWindowHelper(window);
+}
+
+bool DownloadUrl(const std::string& url, HWND caller_window) {
+ return false;
+}
+
+bool GetPluginFinderURL(std::string* plugin_finder_url) {
+ return false;
+}
+
+bool IsDefaultPluginEnabled() {
+ return false;
+}
+
+std::wstring GetWebKitLocale() {
+ return L"en-US";
+}
+
+} // namespace webkit_glue
diff --git a/webkit/tools/test_shell/test_shell.h b/webkit/tools/test_shell/test_shell.h
new file mode 100644
index 0000000..b9b110a8
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
+
+#pragma once
+
+#include <string>
+#include <list>
+
+#include "base/ref_counted.h"
+#include "webkit/tools/test_shell/event_sending_controller.h"
+#include "webkit/tools/test_shell/layout_test_controller.h"
+#include "webkit/tools/test_shell/resource.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+#include "webkit/tools/test_shell/text_input_controller.h"
+#include "webkit/tools/test_shell/test_webview_delegate.h"
+#include "webkit/tools/test_shell/webview_host.h"
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+typedef std::list<HWND> WindowList;
+
+struct WebPreferences;
+class NavigationEntry;
+class TestNavigationController;
+
+class TestShell {
+public:
+ struct TestParams {
+ // Load the test defaults.
+ TestParams() : dump_tree(true), dump_pixels(false) {
+ }
+
+ // The kind of output we want from this test.
+ bool dump_tree;
+ bool dump_pixels;
+
+ // Filename we dump pixels to (when pixel testing is enabled).
+ std::wstring pixel_file_name;
+ };
+
+ TestShell();
+ virtual ~TestShell();
+
+ // Initialization and clean up of logging.
+ static void InitLogging(bool suppress_error_dialogs);
+ static void CleanupLogging();
+
+ // Initialization and clean up of a static member variable.
+ static void InitializeTestShell(bool interactive);
+ static void ShutdownTestShell();
+
+ static bool interactive() { return interactive_; }
+
+ WebView* webView() {
+ return m_webViewHost.get() ? m_webViewHost->webview() : NULL;
+ }
+ WebViewHost* webViewHost() { return m_webViewHost.get(); }
+ WebWidget* popup() { return m_popupHost ? m_popupHost->webwidget() : NULL; }
+ WebWidgetHost* popupHost() { return m_popupHost; }
+
+ // Called by the LayoutTestController to signal test completion.
+ void TestFinished();
+
+ // Called to block the calling thread until TestFinished is called.
+ void WaitTestFinished();
+
+ void Show(WebView* webview, WindowOpenDisposition disposition);
+
+ // We use this to avoid relying on Windows focus during non-interactive
+ // mode.
+ void SetFocus(WebWidgetHost* host, bool enable);
+
+ LayoutTestController* layout_test_controller() {
+ return layout_test_controller_.get();
+ }
+ TestWebViewDelegate* delegate() { return delegate_.get(); }
+ TestNavigationController* navigation_controller() {
+ return navigation_controller_.get();
+ }
+
+ // Resets the LayoutTestController and EventSendingController. Should be
+ // called before loading a page, since some end-editing event notifications
+ // may arrive after the previous page has finished dumping its text and
+ // therefore end up in the next test's results if the messages are still
+ // enabled.
+ void ResetTestController() {
+ layout_test_controller_->Reset();
+ event_sending_controller_->Reset();
+ }
+
+ // Passes options from LayoutTestController through to the delegate (or
+ // any other caller).
+ bool ShouldDumpEditingCallbacks() {
+ return !interactive_ &&
+ layout_test_controller_->ShouldDumpEditingCallbacks();
+ }
+ bool ShouldDumpFrameLoadCallbacks() {
+ return !interactive_ && (test_is_preparing_ || test_is_pending_) &&
+ layout_test_controller_->ShouldDumpFrameLoadCallbacks();
+ }
+ bool ShouldDumpResourceLoadCallbacks() {
+ return !interactive_ && (test_is_preparing_ || test_is_pending_) &&
+ layout_test_controller_->ShouldDumpResourceLoadCallbacks();
+ }
+ bool ShouldDumpTitleChanges() {
+ return !interactive_ &&
+ layout_test_controller_->ShouldDumpTitleChanges();
+ }
+ bool AcceptsEditing() {
+ return layout_test_controller_->AcceptsEditing();
+ }
+
+ void LoadURL(const wchar_t* url);
+ void LoadURLForFrame(const wchar_t* url, const wchar_t* frame_name);
+ void GoBackOrForward(int offset);
+ void Reload();
+ bool Navigate(const NavigationEntry& entry, bool reload);
+
+ bool PromptForSaveFile(const wchar_t* prompt_title, std::wstring* result);
+ std::wstring GetDocumentText();
+ void DumpDocumentText();
+ void DumpRenderTree();
+
+ HWND mainWnd() const { return m_mainWnd; }
+ HWND webViewWnd() const { return m_webViewHost->window_handle(); }
+ HWND editWnd() const { return m_editWnd; }
+ HWND popupWnd() const { return m_popupHost->window_handle(); }
+
+ static WindowList* windowList() { return window_list_; }
+
+ // If shell is non-null, then *shell is assigned upon successful return
+ static bool CreateNewWindow(const std::wstring& startingURL,
+ TestShell** shell = NULL);
+
+ // Implements CreateWebView for TestWebViewDelegate, which in turn
+ // is called as a WebViewDelegate.
+ WebView* CreateWebView(WebView* webview);
+ WebWidget* CreatePopupWidget(WebView* webview);
+ void ClosePopup();
+
+ static ATOM RegisterWindowClass();
+
+ // Called by the WebView delegate WindowObjectCleared() method, this
+ // binds the layout_test_controller_ and other C++ controller classes to
+ // window JavaScript objects so they can be accessed by layout tests.
+ virtual void BindJSObjectsToWindow(WebFrame* frame);
+
+ // Runs a layout test. Loads a single file into the first available
+ // window, then dumps the requested text representation to stdout.
+ // Returns false if the test cannot be run because no windows are open.
+ static bool RunFileTest(const char* filename, const TestParams& params);
+
+ // Writes the back-forward list data for every open window into result.
+ static void DumpBackForwardList(std::wstring* result);
+
+ // Writes the image captured from the given web frame to the given file.
+ // The returned string is the ASCII-ized MD5 sum of the image.
+ static std::string DumpImage(WebFrame* web_frame,
+ const std::wstring& file_name);
+
+ static void ResetWebPreferences();
+
+ WebPreferences* GetWebPreferences() { return web_prefs_; }
+
+ // Some layout tests hardcode a file:///tmp/LayoutTests URL. We get around
+ // this by substituting "tmp" with the path to the LayoutTests parent dir.
+ static std::string RewriteLocalUrl(const std::string& url);
+
+ // Set the timeout for running a test.
+ static void SetFileTestTimeout(int timeout_ms) {
+ file_test_timeout_ms_ = timeout_ms;
+ }
+
+ // Get the timeout for running a test.
+ static int GetFileTestTimeout() { return file_test_timeout_ms_; }
+
+ // Access to the finished event. Used by the static WatchDog
+ // thread.
+ HANDLE finished_event() { return finished_event_; }
+
+ // Have the shell print the StatsTable to stdout on teardown.
+ void DumpStatsTableOnExit() { dump_stats_table_on_exit_ = true; }
+
+ void CallJSGC();
+
+ void set_is_modal(bool value) { is_modal_ = value; }
+ bool is_modal() const { return is_modal_; }
+
+protected:
+ bool Initialize(const std::wstring& startingURL);
+ void SizeToDefault();
+ void SizeTo(int width, int height);
+ void ResizeSubViews();
+
+ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK EditWndProc(HWND, UINT, WPARAM, LPARAM);
+
+protected:
+ HWND m_mainWnd;
+ HWND m_editWnd;
+ scoped_ptr<WebViewHost> m_webViewHost;
+ WebWidgetHost* m_popupHost;
+ WNDPROC default_edit_wnd_proc_;
+
+ // Primitive focus controller for layout test mode.
+ WebWidgetHost* m_focusedWidgetHost;
+
+private:
+ // A set of all our windows.
+ static WindowList* window_list_;
+
+ static HINSTANCE instance_handle_;
+
+ // False when the app is being run using the --layout-tests switch.
+ static bool interactive_;
+
+ // Timeout for page load when running non-interactive file tests, in ms.
+ static int file_test_timeout_ms_;
+
+ scoped_ptr<LayoutTestController> layout_test_controller_;
+
+ scoped_ptr<EventSendingController> event_sending_controller_;
+
+ scoped_ptr<TextInputController> text_input_controller_;
+
+ scoped_ptr<TestNavigationController> navigation_controller_;
+
+ scoped_refptr<TestWebViewDelegate> delegate_;
+
+ // True while a test is preparing to run
+ bool test_is_preparing_;
+
+ // True while a test is running
+ bool test_is_pending_;
+
+ // True if driven from a nested message loop.
+ bool is_modal_;
+
+ // The preferences for the test shell.
+ static WebPreferences* web_prefs_;
+
+ // Used by the watchdog to know when it's finished.
+ HANDLE finished_event_;
+
+ // Dump the stats table counters on exit.
+ bool dump_stats_table_on_exit_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
diff --git a/webkit/tools/test_shell/test_shell.vcproj b/webkit/tools/test_shell/test_shell.vcproj
new file mode 100644
index 0000000..06fd305
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.vcproj
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell"
+ ProjectGUID="{FA39524D-3067-4141-888D-28A86C66F2B9}"
+ RootNamespace="test_shell"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ <ToolFile
+ RelativePath="..\..\build\font_file_copy.rules"
+ />
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\test_shell.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="Font file copy"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\test_shell.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="Font file copy"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="resources"
+ >
+ <File
+ RelativePath=".\resources\fonts\ahem-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Ahem.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Apple_Chancery.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Apple_Symbols.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\AppleMyungjo.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Arial.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Arialb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Ariali.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\comic_sans_ms-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Comic_Sans_MS.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Comic_Sans_MSb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\courier-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courier.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courier_New.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courierb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courierbi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courieri.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Geeza_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Geneva.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgia.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgiab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgiai.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helvetica.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helvetica_Neueb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticabi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticai.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Kaku_Gothic_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Mincho_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Mincho_Prob.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Impact.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\lucida_grande-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Lucida_Grande.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Lucida_Grandeb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Monaco.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_Gothic.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_PGothic.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_PMincho.afm"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\net\base\net_resources.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Osaka.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Papyrus.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\small.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\STKaiti.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Symbol.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\times-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times_New_Roman.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times_New_Romanb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesbi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Trebuchet_MS.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdana.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanabi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanai.afm"
+ >
+ </File>
+ <File
+ RelativePath="$(IntDir)\..\localized_strings\webkit_strings_en-US.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Zapf_Dingbats.afm"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="temp"
+ >
+ <File
+ RelativePath=".\temp\navigation_controller_base.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\temp\navigation_controller_base.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\navigation_entry.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\page_transition_types.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\drag_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\simple_resource_loader_bridge.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_main.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/webkit/tools/test_shell/test_shell.vsprops b/webkit/tools/test_shell/test_shell.vsprops
new file mode 100644
index 0000000..f2f0b9a
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.vsprops
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\breakpad\using_breakpad.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops;$(SolutionDir)..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(OutDir)\WebKit&quot;;&quot;$(SolutionDir)&quot;;&quot;$(IntDir)\..\localized_strings&quot;;&quot;$(SolutionDir)webkit\port\bridge&quot;;&quot;$(SolutionDir)webkit\port\platform&quot;;&quot;$(SolutionDir)webkit\port\platform\network&quot;;&quot;$(SolutionDir)webkit\glue&quot;;&quot;$(SolutionDir)third_party\webkit\src\&quot;;&quot;$(OutDir)\obj\WebCore\JavaScriptHeaders&quot;;&quot;$(OutDir)&quot;"
+ PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib shlwapi.lib rpcrt4.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;$(OutDir)&quot;"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(IntDir)\..\&quot;"
+ />
+</VisualStudioPropertySheet>
diff --git a/webkit/tools/test_shell/test_shell_main.cc b/webkit/tools/test_shell/test_shell_main.cc
new file mode 100644
index 0000000..0256fed
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_main.cc
@@ -0,0 +1,360 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Creates an instance of the test_shell.
+
+#include <stdlib.h> // required by _set_abort_behavior
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/event_recorder.h"
+#include "base/file_util.h"
+#include "base/fixed_string.h"
+#include "base/gfx/native_theme.h"
+#include "base/icu_util.h"
+#include "base/memory_debug.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/resource_util.h"
+#include "base/stats_table.h"
+#include "base/string_util.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_module.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/foreground_helper.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+#include "webkit/tools/test_shell/test_shell_switches.h"
+
+// This is only set for layout tests.
+static wchar_t g_currentTestName[MAX_PATH];
+
+namespace {
+
+// StatsTable initialization parameters.
+static wchar_t* kStatsFile = L"testshell";
+static int kStatsFileThreads = 20;
+static int kStatsFileCounters = 200;
+
+std::string GetDataResource(HMODULE module, int resource_id) {
+ void* data_ptr;
+ size_t data_size;
+ return base::GetDataResourceFromModule(module, resource_id, &data_ptr,
+ &data_size) ?
+ std::string(static_cast<char*>(data_ptr), data_size) : std::string();
+}
+
+// This is called indirectly by the network layer to access resources.
+std::string NetResourceProvider(int key) {
+ return GetDataResource(::GetModuleHandle(NULL), key);
+}
+
+void SetCurrentTestName(char* path)
+{
+ char* lastSlash = strrchr(path, '/');
+ if (lastSlash) {
+ ++lastSlash;
+ } else {
+ lastSlash = path;
+ }
+
+ wcscpy_s(g_currentTestName, arraysize(g_currentTestName),
+ UTF8ToWide(lastSlash).c_str());
+}
+
+bool MinidumpCallback(const wchar_t *dumpPath,
+ const wchar_t *minidumpID,
+ void *context,
+ EXCEPTION_POINTERS *exinfo,
+ MDRawAssertionInfo *assertion,
+ bool succeeded)
+{
+ // Warning: Don't use the heap in this function. It may be corrupted.
+ if (!g_currentTestName[0])
+ return false;
+
+ // Try to rename the minidump file to include the crashed test's name.
+ FixedString<wchar_t, MAX_PATH> origPath;
+ origPath.Append(dumpPath);
+ origPath.Append(file_util::kPathSeparator);
+ origPath.Append(minidumpID);
+ origPath.Append(L".dmp");
+
+ FixedString<wchar_t, MAX_PATH> newPath;
+ newPath.Append(dumpPath);
+ newPath.Append(file_util::kPathSeparator);
+ newPath.Append(g_currentTestName);
+ newPath.Append(L"-");
+ newPath.Append(minidumpID);
+ newPath.Append(L".dmp");
+
+ // May use the heap, but oh well. If this fails, we'll just have the
+ // original dump file lying around.
+ _wrename(origPath.get(), newPath.get());
+
+ return false;
+}
+} // namespace
+
+int main(int argc, char* argv[])
+{
+#ifdef _CRTDBG_MAP_ALLOC
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+#endif
+
+ CommandLine parsed_command_line;
+ if (parsed_command_line.HasSwitch(test_shell::kStartupDialog))
+ MessageBox(NULL, L"attach to me?", L"test_shell", MB_OK);
+ //webkit_glue::SetLayoutTestMode(true);
+
+ // Allocate a message loop for this thread. Although it is not used
+ // directly, its constructor sets up some necessary state.
+ MessageLoop main_message_loop;
+
+ bool suppress_error_dialogs =
+ (GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0) ||
+ parsed_command_line.HasSwitch(test_shell::kNoErrorDialogs) ||
+ parsed_command_line.HasSwitch(test_shell::kLayoutTests));
+ TestShell::InitLogging(suppress_error_dialogs);
+
+ // Suppress abort message in v8 library in debugging mode.
+ // V8 calls abort() when it hits assertion errors.
+ if (suppress_error_dialogs) {
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+ }
+
+ bool layout_test_mode =
+ parsed_command_line.HasSwitch(test_shell::kLayoutTests);
+
+ net::HttpCache::Mode cache_mode = net::HttpCache::NORMAL;
+ bool playback_mode =
+ parsed_command_line.HasSwitch(test_shell::kPlaybackMode);
+ bool record_mode =
+ parsed_command_line.HasSwitch(test_shell::kRecordMode);
+
+ if (playback_mode)
+ cache_mode = net::HttpCache::PLAYBACK;
+ else if (record_mode)
+ cache_mode = net::HttpCache::RECORD;
+
+ if (layout_test_mode ||
+ parsed_command_line.HasSwitch(test_shell::kEnableFileCookies))
+ CookieMonster::EnableFileScheme();
+
+ std::wstring cache_path =
+ parsed_command_line.GetSwitchValue(test_shell::kCacheDir);
+ if (cache_path.empty()) {
+ PathService::Get(base::DIR_EXE, &cache_path);
+ file_util::AppendToPath(&cache_path, L"cache");
+ }
+
+ // Initializing with a default context, which means no on-disk cookie DB,
+ // and no support for directory listings.
+ SimpleResourceLoaderBridge::Init(
+ new TestShellRequestContext(cache_path, cache_mode));
+
+ // Load ICU data tables
+ icu_util::Initialize();
+
+ // Config the network module so it has access to a limited set of resources.
+ NetModule::SetResourceProvider(NetResourceProvider);
+
+ INITCOMMONCONTROLSEX InitCtrlEx;
+
+ InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
+ InitCommonControlsEx(&InitCtrlEx);
+
+ bool interactive = !layout_test_mode;
+ TestShell::InitializeTestShell(interactive);
+
+ // Disable user themes for layout tests so pixel tests are consistent.
+ if (!interactive)
+ gfx::NativeTheme::instance()->DisableTheming();
+
+ if (parsed_command_line.HasSwitch(test_shell::kTestShellTimeOut)) {
+ const std::wstring timeout_str = parsed_command_line.GetSwitchValue(
+ test_shell::kTestShellTimeOut);
+ int timeout_ms = static_cast<int>(StringToInt64(timeout_str.c_str()));
+ if (timeout_ms > 0)
+ TestShell::SetFileTestTimeout(timeout_ms);
+ }
+
+ // Initialize global strings
+ TestShell::RegisterWindowClass();
+
+ // Treat the first loose value as the initial URL to open.
+ std::wstring uri;
+
+ // Default to a homepage if we're interactive.
+ if (interactive) {
+ PathService::Get(base::DIR_SOURCE_ROOT, &uri);
+ file_util::AppendToPath(&uri, L"webkit");
+ file_util::AppendToPath(&uri, L"data");
+ file_util::AppendToPath(&uri, L"test_shell");
+ file_util::AppendToPath(&uri, L"index.html");
+ }
+
+ if (parsed_command_line.GetLooseValueCount() > 0) {
+ CommandLine::LooseValueIterator iter = parsed_command_line.GetLooseValuesBegin();
+ uri = *iter;
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kCrashDumps)) {
+ std::wstring dir = parsed_command_line.GetSwitchValue(test_shell::kCrashDumps);
+ new google_breakpad::ExceptionHandler(dir, 0, &MinidumpCallback, 0, true);
+ }
+
+ std::wstring js_flags =
+ parsed_command_line.GetSwitchValue(test_shell::kJavaScriptFlags);
+ // Test shell always exposes the GC.
+ CommandLine::AppendSwitch(&js_flags, L"expose-gc");
+ webkit_glue::SetJavaScriptFlags(js_flags);
+
+ // load and initialize the stats table.
+ StatsTable *table = new StatsTable(kStatsFile, kStatsFileThreads, kStatsFileCounters);
+ StatsTable::set_current(table);
+
+ TestShell* shell;
+ if (TestShell::CreateNewWindow(uri, &shell)) {
+ if (record_mode || playback_mode) {
+ // Move the window to the upper left corner for consistent
+ // record/playback mode. For automation, we want this to work
+ // on build systems where the script invoking us is a background
+ // process. So for this case, make our window the topmost window
+ // as well.
+ ForegroundHelper::SetForeground(shell->mainWnd());
+ ::SetWindowPos(shell->mainWnd(), HWND_TOP, 0, 0, 600, 800, 0);
+ // Tell webkit as well.
+ webkit_glue::SetRecordPlaybackMode(true);
+ }
+
+ shell->Show(shell->webView(), NEW_WINDOW);
+
+ if (parsed_command_line.HasSwitch(test_shell::kDumpStatsTable))
+ shell->DumpStatsTableOnExit();
+
+ bool no_events = parsed_command_line.HasSwitch(test_shell::kNoEvents);
+ if ((record_mode || playback_mode) && !no_events) {
+ std::wstring script_path = cache_path;
+ // Create the cache directory in case it doesn't exist.
+ file_util::CreateDirectory(cache_path);
+ file_util::AppendToPath(&script_path, L"script.log");
+ if (record_mode)
+ base::EventRecorder::current()->StartRecording(script_path);
+ if (playback_mode)
+ base::EventRecorder::current()->StartPlayback(script_path);
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kDebugMemoryInUse)) {
+ base::MemoryDebug::SetMemoryInUseEnabled(true);
+ // Dump all in use memory at startup
+ base::MemoryDebug::DumpAllMemoryInUse();
+ }
+
+ // See if we need to run the tests.
+ if (layout_test_mode) {
+ webkit_glue::SetLayoutTestMode(true);
+
+ // Set up for the kind of test requested.
+ TestShell::TestParams params;
+ if (parsed_command_line.HasSwitch(test_shell::kDumpPixels)) {
+ // The pixel test flag also gives the image file name to use.
+ params.dump_pixels = true;
+ params.pixel_file_name = parsed_command_line.GetSwitchValue(
+ test_shell::kDumpPixels);
+ if (params.pixel_file_name.size() == 0) {
+ fprintf(stderr, "No file specified for pixel tests");
+ exit(1);
+ }
+ }
+ if (parsed_command_line.HasSwitch(test_shell::kNoTree)) {
+ params.dump_tree = false;
+ }
+
+ if (uri.length() == 0) {
+ // Watch stdin for URLs.
+ char filenameBuffer[2048];
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char *newLine = strchr(filenameBuffer, '\n');
+ if (newLine)
+ *newLine = '\0';
+ if (!*filenameBuffer)
+ continue;
+
+ SetCurrentTestName(filenameBuffer);
+
+ if (!TestShell::RunFileTest(filenameBuffer, params))
+ break;
+ }
+ } else {
+ TestShell::RunFileTest(WideToUTF8(uri).c_str(), params);
+ }
+
+ shell->CallJSGC();
+ shell->CallJSGC();
+ if (shell) delete shell;
+ } else {
+ MessageLoop::current()->Run();
+ }
+
+ // Flush any remaining messages. This ensures that any
+ // accumulated Task objects get destroyed before we exit,
+ // which avoids noise in purify leak-test results.
+ MessageLoop::current()->Quit();
+ MessageLoop::current()->Run();
+
+ if (record_mode)
+ base::EventRecorder::current()->StopRecording();
+ if (playback_mode)
+ base::EventRecorder::current()->StopPlayback();
+ }
+
+ TestShell::ShutdownTestShell();
+ TestShell::CleanupLogging();
+
+ // Tear down shared StatsTable; prevents unit_tests from leaking it.
+ StatsTable::set_current(NULL);
+ delete table;
+
+#ifdef _CRTDBG_MAP_ALLOC
+ _CrtDumpMemoryLeaks();
+#endif
+ return 0;
+}
+
diff --git a/webkit/tools/test_shell/test_shell_request_context.cc b/webkit/tools/test_shell/test_shell_request_context.cc
new file mode 100644
index 0000000..0406e01
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_request_context.cc
@@ -0,0 +1,69 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+
+#include "net/base/cookie_monster.h"
+#include "webkit/glue/webkit_glue.h"
+
+TestShellRequestContext::TestShellRequestContext() {
+ Init(std::wstring(), net::HttpCache::NORMAL);
+}
+
+TestShellRequestContext::TestShellRequestContext(
+ const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode) {
+ Init(cache_path, cache_mode);
+}
+
+void TestShellRequestContext::Init(
+ const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode) {
+ cookie_store_ = new CookieMonster();
+
+ user_agent_ = webkit_glue::GetDefaultUserAgent();
+
+ // hard-code A-L and A-C for test shells
+ accept_language_ = "en-us,en";
+ accept_charset_ = "iso-8859-1,*,utf-8";
+
+ net::HttpCache *cache;
+ if (cache_path.empty()) {
+ cache = new net::HttpCache(NULL, 0);
+ } else {
+ cache = new net::HttpCache(NULL, cache_path, 0);
+ }
+ cache->set_mode(cache_mode);
+ http_transaction_factory_ = cache;
+}
+
+TestShellRequestContext::~TestShellRequestContext() {
+ delete cookie_store_;
+ delete http_transaction_factory_;
+}
diff --git a/webkit/tools/test_shell/test_shell_request_context.h b/webkit/tools/test_shell/test_shell_request_context.h
new file mode 100644
index 0000000..255fb64
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_request_context.h
@@ -0,0 +1,53 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
+
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+
+// A basic URLRequestContext that only provides an in-memory cookie store.
+class TestShellRequestContext : public URLRequestContext {
+ public:
+ // Use an in-memory cache
+ TestShellRequestContext();
+
+ // Use an on-disk cache at the specified location. Optionally, use the cache
+ // in playback or record mode.
+ TestShellRequestContext(const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode);
+
+ ~TestShellRequestContext();
+
+ private:
+ void Init(const std::wstring& cache_path, net::HttpCache::Mode cache_mode);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
diff --git a/webkit/tools/test_shell/test_shell_switches.cc b/webkit/tools/test_shell/test_shell_switches.cc
new file mode 100644
index 0000000..3560d60
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_switches.cc
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/test_shell_switches.h"
+
+namespace test_shell {
+
+// Suppresses all error dialogs when present.
+const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
+
+// Causes the test_shell to run using stdin and stdout for URLs and output,
+// respectively, and interferes with interactive use of the UI.
+const wchar_t kLayoutTests[] = L"layout-tests";
+const wchar_t kCrashDumps[] = L"crash-dumps"; // Enable crash dumps
+
+// Command line flags that control the tests when layout-tests is specified.
+const wchar_t kNoTree[] = L"notree"; // Don't dump the render tree.
+const wchar_t kDumpPixels[] = L"pixel-tests"; // Enable pixel tests.
+// Optional command line switch that specifies timeout time for page load when
+// running non-interactive file tests, in ms.
+const wchar_t kTestShellTimeOut[] = L"time-out-ms";
+
+const wchar_t kStartupDialog[] = L"testshell-startup-dialog";
+
+// JavaScript flags passed to engine.
+const wchar_t kJavaScriptFlags[] = L"js-flags";
+
+// Run the http cache in record mode.
+const wchar_t kRecordMode[] = L"record-mode";
+
+// Run the http cache in playback mode.
+const wchar_t kPlaybackMode[] = L"playback-mode";
+
+// Don't record/playback events when using record & playback.
+const wchar_t kNoEvents[] = L"no-events";
+
+// Dump stats table on exit.
+const wchar_t kDumpStatsTable[] = L"stats";
+
+// Use a specified cache directory.
+const wchar_t kCacheDir[] = L"cache-dir";
+
+// When being run through a memory profiler, trigger memory in use dumps at
+// startup and just prior to shutdown.
+const wchar_t kDebugMemoryInUse[] = L"debug-memory-in-use";
+
+// Enable cookies on the file:// scheme. --layout-tests also enables this.
+const wchar_t kEnableFileCookies[] = L"enable-file-cookies";
+
+} // namespace test_shell
diff --git a/webkit/tools/test_shell/test_shell_switches.h b/webkit/tools/test_shell/test_shell_switches.h
new file mode 100644
index 0000000..c92ce59
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_switches.h
@@ -0,0 +1,55 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines all the command-line switches used by Chrome.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
+
+namespace test_shell {
+
+extern const wchar_t kCrashDumps[];
+extern const wchar_t kDumpPixels[];
+extern const wchar_t kLayoutTests[];
+extern const wchar_t kNoErrorDialogs[];
+extern const wchar_t kNoTree[];
+extern const wchar_t kTestShellTimeOut[];
+extern const wchar_t kStartupDialog[];
+extern const wchar_t kJavaScriptFlags[];
+extern const wchar_t kRecordMode[];
+extern const wchar_t kPlaybackMode[];
+extern const wchar_t kNoEvents[];
+extern const wchar_t kDumpStatsTable[];
+extern const wchar_t kCacheDir[];
+extern const wchar_t kDebugMemoryInUse[];
+extern const wchar_t kEnableFileCookies[];
+
+} // namespace test_shell
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
diff --git a/webkit/tools/test_shell/test_shell_test.cc b/webkit/tools/test_shell/test_shell_test.cc
new file mode 100644
index 0000000..8b433d2
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_test.cc
@@ -0,0 +1,63 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/test_shell_test.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+
+std::wstring TestShellTest::GetTestURL(std::wstring test_case_path,
+ const std::wstring& test_case) {
+ file_util::AppendToPath(&test_case_path, test_case);
+ return test_case_path;
+}
+
+void TestShellTest::SetUp() {
+ // Make a test shell for use by the test.
+ TestShell::RegisterWindowClass();
+ CreateEmptyWindow();
+ test_shell_->Show(test_shell_->webView(), NEW_WINDOW);
+
+ // Point data_dir_ to the root of the test case dir
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_));
+ file_util::AppendToPath(&data_dir_, L"webkit");
+ file_util::AppendToPath(&data_dir_, L"data");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+}
+
+void TestShellTest::TearDown() {
+ // Loading a blank url clears the memory in the current page.
+ test_shell_->LoadURL(L"about:blank");
+ DestroyWindow(test_shell_->mainWnd());
+ LayoutTestController::ClearShell();
+}
+
+void TestShellTest::CreateEmptyWindow() {
+ TestShell::CreateNewWindow(L"about:blank", &test_shell_);
+} \ No newline at end of file
diff --git a/webkit/tools/test_shell/test_shell_test.h b/webkit/tools/test_shell/test_shell_test.h
new file mode 100644
index 0000000..b48e22d
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_test.h
@@ -0,0 +1,63 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Base test class used by all test shell tests. Provides boiler plate
+ * code to create and destroy a new test shell for each gTest test.
+ */
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
+
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestShellTest : public testing::Test {
+ protected:
+ // Returns the path "test_case_path/test_case".
+ std::wstring GetTestURL(std::wstring test_case_path,
+ const std::wstring& test_case);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ // Don't refactor away; some unittests override this!
+ virtual void CreateEmptyWindow();
+
+ static const char* kJavascriptDelayExitScript;
+
+ protected:
+ // Location of SOURCE_ROOT/webkit/data/
+ std::wstring data_dir_;
+
+ TestShell* test_shell_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
diff --git a/webkit/tools/test_shell/test_shell_tests.vcproj b/webkit/tools/test_shell/test_shell_tests.vcproj
new file mode 100644
index 0000000..569dbd0
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_tests.vcproj
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell_tests"
+ ProjectGUID="{E6766F81-1FCD-4CD7-BC16-E36964A14867}"
+ RootNamespace="test_shell_tests"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\test_shell_tests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\test_shell_tests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="support"
+ >
+ <File
+ RelativePath=".\drag_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\image_decoder_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\image_decoder_unittest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\navigation_controller_base.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\net\base\net_resources.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\run_all_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\simple_resource_loader_bridge.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\small.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_test.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_test.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="tests"
+ >
+ <File
+ RelativePath="..\..\glue\autocomplete_input_listener_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\bmp\BMPImageDecoder_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\bookmarklet_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\context_menu_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\cpp_bound_class_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\cpp_variant_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\dom_operations_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\dom_serializer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\GKURL_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\glue_serialize_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\ico\ICOImageDecoder_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\iframe_redirect_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\keyboard_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\mimetype_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\multipart_response_delegate_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\node_leak_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\password_autocomplete_listener_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\plugin_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\regular_expression_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\resource_fetcher_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\webplugin_impl_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\xbm\XBMImageDecoder_unittest.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/webkit/tools/test_shell/test_shell_tests.vsprops b/webkit/tools/test_shell/test_shell_tests.vsprops
new file mode 100644
index 0000000..0e11457
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_tests.vsprops
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell_tests"
+ InheritedPropertySheets=".\test_shell.vsprops;..\..\build\webkit_common.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(SolutionDir)..\v8\public"
+ PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/webkit/tools/test_shell/test_webview_delegate.cc b/webkit/tools/test_shell/test_webview_delegate.cc
new file mode 100644
index 0000000..231415f
--- /dev/null
+++ b/webkit/tools/test_shell/test_webview_delegate.cc
@@ -0,0 +1,943 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains the implementation of TestWebViewDelegate, which serves
+// as the WebViewDelegate for the TestShellWebHost. The host is expected to
+// have initialized a MessageLoop before these methods are called.
+
+#include "webkit/tools/test_shell/test_webview_delegate.h"
+
+#include <objidl.h>
+#include <shlobj.h>
+
+#include "base/gfx/point.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "webkit/glue/webdatasource.h"
+#include "webkit/glue/webdropdata.h"
+#include "webkit/glue/weberror.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/webview.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/drag_delegate.h"
+#include "webkit/tools/test_shell/drop_delegate.h"
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+namespace {
+
+static int next_page_id_ = 1;
+
+// Used to write a platform neutral file:/// URL by only taking the filename
+// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt").
+std::wstring UrlSuitableForTestResult(const std::wstring& url) {
+ if (url.empty() || std::wstring::npos == url.find(L"file://"))
+ return url;
+
+ return PathFindFileNameW(url.c_str());
+}
+
+// Adds a file called "DRTFakeFile" to |data_object| (CF_HDROP). Use to fake
+// dragging a file.
+void AddDRTFakeFileToDataObject(IDataObject* data_object) {
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ const char filename[] = "DRTFakeFile";
+ const int filename_len = arraysize(filename);
+
+ // Allocate space for the DROPFILES struct, filename, and 2 null characters.
+ medium.hGlobal = GlobalAlloc(GPTR, sizeof(DROPFILES) + filename_len + 2);
+ DCHECK(medium.hGlobal);
+ DROPFILES* drop_files = static_cast<DROPFILES*>(GlobalLock(medium.hGlobal));
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = 0; // Filenames are ascii
+ strcpy_s(reinterpret_cast<char*>(drop_files) + sizeof(DROPFILES),
+ filename_len, filename);
+ GlobalUnlock(medium.hGlobal);
+
+ FORMATETC file_desc_fmt = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ data_object->SetData(&file_desc_fmt, &medium, TRUE);
+}
+
+} // namespace
+
+// WebViewDelegate -----------------------------------------------------------
+
+TestWebViewDelegate::~TestWebViewDelegate() {
+ if (custom_cursor_)
+ DestroyIcon(custom_cursor_);
+ RevokeDragDrop(shell_->webViewWnd());
+}
+
+WebView* TestWebViewDelegate::CreateWebView(WebView* webview,
+ bool user_gesture) {
+ return shell_->CreateWebView(webview);
+}
+
+WebWidget* TestWebViewDelegate::CreatePopupWidget(WebView* webview) {
+ return shell_->CreatePopupWidget(webview);
+}
+
+WebPluginDelegate* TestWebViewDelegate::CreatePluginDelegate(
+ WebView* webview,
+ const GURL& url,
+ const std::string& mime_type,
+ const std::string& clsid,
+ std::string* actual_mime_type) {
+ HWND hwnd = GetContainingWindow(webview);
+ if (!hwnd)
+ return NULL;
+
+ bool allow_wildcard = true;
+ WebPluginInfo info;
+ if (!NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid,
+ allow_wildcard, &info,
+ actual_mime_type))
+ return NULL;
+
+ if (actual_mime_type && !actual_mime_type->empty())
+ return WebPluginDelegateImpl::Create(info.file, *actual_mime_type, hwnd);
+ else
+ return WebPluginDelegateImpl::Create(info.file, mime_type, hwnd);
+}
+
+void TestWebViewDelegate::OpenURL(WebView* webview, const GURL& url,
+ WindowOpenDisposition disposition) {
+ DCHECK_NE(disposition, CURRENT_TAB); // No code for this
+ if (disposition == SUPPRESS_OPEN)
+ return;
+ TestShell* shell;
+ if (TestShell::CreateNewWindow(UTF8ToWide(url.spec()), &shell))
+ shell->Show(shell->webView(), disposition);
+}
+
+void TestWebViewDelegate::DidStartLoading(WebView* webview) {
+ if (page_is_loading_) {
+ LOG(ERROR) << "DidStartLoading called while loading";
+ return;
+ }
+ page_is_loading_ = true;
+}
+
+void TestWebViewDelegate::DidStopLoading(WebView* webview) {
+ if (!page_is_loading_) {
+ LOG(ERROR) << "DidStopLoading called while not loading";
+ return;
+ }
+ page_is_loading_ = false;
+}
+
+void TestWebViewDelegate::WindowObjectCleared(WebFrame* webframe) {
+ shell_->BindJSObjectsToWindow(webframe);
+}
+
+WindowOpenDisposition TestWebViewDelegate::DispositionForNavigationAction(
+ WebView* webview,
+ WebFrame* frame,
+ const WebRequest* request,
+ WebNavigationType type,
+ WindowOpenDisposition disposition,
+ bool is_redirect) {
+ if (is_custom_policy_delegate_) {
+ std::wstring frame_name = frame->GetName();
+ printf("Policy delegate: attempt to load %s\n",
+ request->GetURL().spec().c_str());
+ return IGNORE_ACTION;
+ } else {
+ return WebViewDelegate::DispositionForNavigationAction(
+ webview, frame, request, type, disposition, is_redirect);
+ }
+}
+
+void TestWebViewDelegate::SetCustomPolicyDelegate(bool isCustom) {
+ is_custom_policy_delegate_ = isCustom;
+}
+
+void TestWebViewDelegate::AssignIdentifierToRequest(WebView* webview,
+ uint32 identifier,
+ const WebRequest& request) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ resource_identifier_map_[identifier] = request.GetURL().possibly_invalid_spec();
+ }
+}
+
+std::string TestWebViewDelegate::GetResourceDescription(uint32 identifier) {
+ ResourceMap::iterator it = resource_identifier_map_.find(identifier);
+ return it != resource_identifier_map_.end() ? it->second : "<unknown>";
+}
+
+void TestWebViewDelegate::WillSendRequest(WebView* webview,
+ uint32 identifier,
+ WebRequest* request) {
+ std::string request_url = request->GetURL().possibly_invalid_spec();
+
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - willSendRequest <WebRequest URL \"%s\">\n",
+ GetResourceDescription(identifier).c_str(),
+ request_url.c_str());
+ }
+
+ // Set the new substituted URL.
+ request->SetURL(GURL(TestShell::RewriteLocalUrl(request_url)));
+}
+
+void TestWebViewDelegate::DidFinishLoading(WebView* webview,
+ uint32 identifier) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - didFinishLoading\n",
+ GetResourceDescription(identifier).c_str());
+ }
+
+ resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidFailLoadingWithError(WebView* webview,
+ uint32 identifier,
+ const WebError& error) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - didFailLoadingWithError <WebError code %d,"
+ " failing URL \"%s\">\n",
+ GetResourceDescription(identifier).c_str(),
+ error.GetErrorCode(),
+ error.GetFailedURL().spec().c_str());
+ }
+
+ resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidStartProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame,
+ NavigationGesture gesture) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didStartProvisionalLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (!top_loading_frame_) {
+ top_loading_frame_ = frame;
+ }
+ UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidReceiveServerRedirectForProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveServerRedirectForProvisionalLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidFailProvisionalLoadWithError(
+ WebView* webview,
+ const WebError& error,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFailProvisionalLoadWithError\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (page_is_loading_)
+ DidStopLoading(webview);
+ LocationChangeDone(frame->GetProvisionalDataSource());
+
+ // Don't display an error page if we're running layout tests, because
+ // DumpRenderTree doesn't.
+ if (!shell_->interactive())
+ return;
+
+ // Don't display an error page if this is simply a cancelled load. Aside
+ // from being dumb, WebCore doesn't expect it and it will cause a crash.
+ if (error.GetErrorCode() == net::ERR_ABORTED)
+ return;
+
+ const WebRequest& failed_request =
+ frame->GetProvisionalDataSource()->GetRequest();
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(failed_request.GetExtraData());
+ bool replace = extra_data && extra_data->pending_page_id != -1;
+
+ scoped_ptr<WebRequest> request(failed_request.Clone());
+ request->SetURL(GURL("testshell-error:"));
+
+ std::string error_text =
+ StringPrintf("Error loading url: %d", error.GetErrorCode());
+
+ frame->LoadAlternateHTMLString(request.get(), error_text,
+ error.GetFailedURL(), replace);
+}
+
+void TestWebViewDelegate::DidCommitLoadForFrame(WebView* webview,
+ WebFrame* frame,
+ bool is_new_navigation) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didCommitLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveTitle(WebView* webview,
+ const std::wstring& title,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveTitle\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (shell_->ShouldDumpTitleChanges()) {
+ printf("TITLE CHANGED: %S\n", title.c_str());
+ }
+}
+
+void TestWebViewDelegate::DidFinishLoadForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFinishLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateAddressBar(webview);
+ LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFailLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFailLoadWithError\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (page_is_loading_)
+ DidStopLoading(webview);
+ LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFinishDocumentLoadForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFinishDocumentLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::DidHandleOnloadEventsForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didHandleOnloadEventsForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::DidChangeLocationWithinPageForFrame(
+ WebView* webview, WebFrame* frame, bool is_new_navigation) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didChangeLocationWithinPageForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveIconForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveIconForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::WillPerformClientRedirect(WebView* webview,
+ WebFrame* frame,
+ const std::wstring& dest_url,
+ unsigned int delay_seconds,
+ unsigned int fire_date) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ // FIXME: prettyprint the url?
+ printf("%S - willPerformClientRedirectToURL: %S\n",
+ GetFrameDescription(frame).c_str(), dest_url.c_str());
+ }
+}
+
+void TestWebViewDelegate::DidCancelClientRedirect(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didCancelClientRedirectForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::AddMessageToConsole(WebView* webview,
+ const std::wstring& message,
+ unsigned int line_no,
+ const std::wstring& source_id) {
+ if (shell_->interactive()) {
+ logging::LogMessage("CONSOLE", 0).stream() << "\""
+ << message.c_str()
+ << ",\" source: "
+ << source_id.c_str()
+ << "("
+ << line_no
+ << ")";
+ } else {
+ // This matches win DumpRenderTree's UIDelegate.cpp.
+ std::wstring new_message = message;
+ if (!message.empty()) {
+ new_message = message;
+ size_t file_protocol = new_message.find(L"file://");
+ if (file_protocol != std::wstring::npos) {
+ new_message = new_message.substr(0, file_protocol) +
+ UrlSuitableForTestResult(new_message);
+ }
+ }
+
+ std::string utf8 = WideToUTF8(new_message);
+ printf("CONSOLE MESSAGE: line %d: %s\n", line_no, utf8.c_str());
+ }
+}
+
+void TestWebViewDelegate::RunJavaScriptAlert(WebView* webview,
+ const std::wstring& message) {
+ if (shell_->interactive()) {
+ MessageBox(shell_->mainWnd(),
+ message.c_str(),
+ L"JavaScript Alert",
+ MB_OK);
+ } else {
+ std::string utf8 = WideToUTF8(message);
+ printf("ALERT: %s\n", utf8.c_str());
+ }
+}
+
+bool TestWebViewDelegate::RunJavaScriptConfirm(WebView* webview,
+ const std::wstring& message) {
+ if (!shell_->interactive()) {
+ // When running tests, write to stdout.
+ std::string utf8 = WideToUTF8(message);
+ printf("CONFIRM: %s\n", utf8.c_str());
+ return true;
+ }
+ return false;
+}
+
+bool TestWebViewDelegate::RunJavaScriptPrompt(WebView* webview,
+ const std::wstring& message, const std::wstring& default_value,
+ std::wstring* result) {
+ if (!shell_->interactive()) {
+ // When running tests, write to stdout.
+ std::string utf8_message = WideToUTF8(message);
+ std::string utf8_default_value = WideToUTF8(default_value);
+ printf("PROMPT: %s, default text: %s\n", utf8_message.c_str(),
+ utf8_default_value.c_str());
+ return true;
+ }
+ return false;
+}
+
+void TestWebViewDelegate::StartDragging(WebView* webview,
+ const WebDropData& drop_data) {
+
+ if (!drag_delegate_)
+ drag_delegate_ = new TestDragDelegate(shell_->webViewWnd(),
+ shell_->webView());
+ if (webkit_glue::IsLayoutTestMode()) {
+ if (shell_->layout_test_controller()->ShouldAddFileToPasteboard()) {
+ // Add a file called DRTFakeFile to the drag&drop clipboard.
+ AddDRTFakeFileToDataObject(drop_data.data_object);
+ }
+
+ // When running a test, we need to fake a drag drop operation otherwise
+ // Windows waits for real mouse events to know when the drag is over.
+ EventSendingController::DoDragDrop(drop_data.data_object);
+ } else {
+ const DWORD ok_effect = DROPEFFECT_COPY | DROPEFFECT_LINK | DROPEFFECT_MOVE;
+ DWORD effect;
+ HRESULT res = DoDragDrop(drop_data.data_object, drag_delegate_.get(),
+ ok_effect, &effect);
+ DCHECK(DRAGDROP_S_DROP == res || DRAGDROP_S_CANCEL == res);
+ }
+ webview->DragSourceSystemDragEnded();
+}
+
+void TestWebViewDelegate::ShowContextMenu(WebView* webview,
+ ContextNode::Type type,
+ int x,
+ int y,
+ const GURL& link_url,
+ const GURL& image_url,
+ const GURL& page_url,
+ const GURL& frame_url,
+ const std::wstring& selection_text,
+ const std::wstring& misspelled_word,
+ int edit_flags) {
+ CapturedContextMenuEvent context(type, x, y);
+ captured_context_menu_events_.push_back(context);
+}
+
+// The output from these methods in non-interactive mode should match that
+// expected by the layout tests. See EditingDelegate.m in DumpRenderTree.
+bool TestWebViewDelegate::ShouldBeginEditing(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n",
+ utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldEndEditing(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n",
+ utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertNode(WebView* webview,
+ std::wstring node,
+ std::wstring range,
+ std::wstring action) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_node = WideToUTF8(node);
+ std::string utf8_range = WideToUTF8(range);
+ std::string utf8_action = WideToUTF8(action);
+ printf("EDITING DELEGATE: shouldInsertNode:%s "
+ "replacingDOMRange:%s givenAction:%s\n",
+ utf8_node.c_str(), utf8_range.c_str(), utf8_action.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertText(WebView* webview,
+ std::wstring text,
+ std::wstring range,
+ std::wstring action) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_text = WideToUTF8(text);
+ std::string utf8_range = WideToUTF8(range);
+ std::string utf8_action = WideToUTF8(action);
+ printf("EDITING DELEGATE: shouldInsertText:%s "
+ "replacingDOMRange:%s givenAction:%s\n",
+ utf8_text.c_str(), utf8_range.c_str(), utf8_action.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldChangeSelectedRange(WebView* webview,
+ std::wstring fromRange,
+ std::wstring toRange,
+ std::wstring affinity,
+ bool stillSelecting) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_from = WideToUTF8(fromRange);
+ std::string utf8_to = WideToUTF8(toRange);
+ std::string utf8_affinity = WideToUTF8(affinity);
+ printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s "
+ "toDOMRange:%s affinity:%s stillSelecting:%s\n",
+ utf8_from.c_str(),
+ utf8_to.c_str(),
+ utf8_affinity.c_str(),
+ (stillSelecting ? "TRUE" : "FALSE"));
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldDeleteRange(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldApplyStyle(WebView* webview,
+ std::wstring style,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_style = WideToUTF8(style);
+ std::string utf8_range = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n",
+ utf8_style.c_str(), utf8_range.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::SmartInsertDeleteEnabled() {
+ return true;
+}
+
+void TestWebViewDelegate::DidBeginEditing() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidChangeSelection() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidChangeContents() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidChange:WebViewDidChangeNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidEndEditing() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidEndEditing:WebViewDidEndEditingNotification\n");
+ }
+}
+
+WebHistoryItem* TestWebViewDelegate::GetHistoryEntryAtOffset(int offset) {
+ TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+ shell_->navigation_controller()->GetEntryAtOffset(offset));
+ if (!entry)
+ return NULL;
+
+ return entry->GetHistoryItem();
+}
+
+void TestWebViewDelegate::GoToEntryAtOffsetAsync(int offset) {
+ shell_->navigation_controller()->GoToOffset(offset);
+}
+
+int TestWebViewDelegate::GetHistoryBackListCount() {
+ int current_index =
+ shell_->navigation_controller()->GetLastCommittedEntryIndex();
+ return current_index;
+}
+
+int TestWebViewDelegate::GetHistoryForwardListCount() {
+ int current_index =
+ shell_->navigation_controller()->GetLastCommittedEntryIndex();
+ return shell_->navigation_controller()->GetEntryCount() - current_index - 1;
+}
+
+void TestWebViewDelegate::SetUserStyleSheetEnabled(bool is_enabled) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->user_style_sheet_enabled = is_enabled;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetUserStyleSheetLocation(const GURL& location) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->user_style_sheet_enabled = true;
+ prefs->user_style_sheet_location = location;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetDashboardCompatibilityMode(bool use_mode) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->dashboard_compatibility_mode = use_mode;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+// WebWidgetDelegate ---------------------------------------------------------
+
+HWND TestWebViewDelegate::GetContainingWindow(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ return host->window_handle();
+
+ return NULL;
+}
+
+void TestWebViewDelegate::DidInvalidateRect(WebWidget* webwidget,
+ const gfx::Rect& rect) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ host->DidInvalidateRect(rect);
+}
+
+void TestWebViewDelegate::DidScrollRect(WebWidget* webwidget, int dx, int dy,
+ const gfx::Rect& clip_rect) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ host->DidScrollRect(dx, dy, clip_rect);
+}
+
+void TestWebViewDelegate::Show(WebWidget* webwidget, WindowOpenDisposition) {
+ if (webwidget == shell_->webView()) {
+ ShowWindow(shell_->mainWnd(), SW_SHOW);
+ UpdateWindow(shell_->mainWnd());
+ } else if (webwidget == shell_->popup()) {
+ ShowWindow(shell_->popupWnd(), SW_SHOW);
+ UpdateWindow(shell_->popupWnd());
+ }
+}
+
+void TestWebViewDelegate::CloseWidgetSoon(WebWidget* webwidget) {
+ if (webwidget == shell_->webView()) {
+ PostMessage(shell_->mainWnd(), WM_CLOSE, 0, 0);
+ } else if (webwidget == shell_->popup()) {
+ shell_->ClosePopup();
+ }
+}
+
+void TestWebViewDelegate::Focus(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ shell_->SetFocus(host, true);
+}
+
+void TestWebViewDelegate::Blur(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ shell_->SetFocus(host, false);
+}
+
+void TestWebViewDelegate::SetCursor(WebWidget* webwidget,
+ const WebCursor& cursor) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget)) {
+ if (custom_cursor_) {
+ DestroyIcon(custom_cursor_);
+ custom_cursor_ = NULL;
+ }
+ if (cursor.type() == WebCursor::CUSTOM) {
+ custom_cursor_ = cursor.GetCustomCursor();
+ host->SetCursor(custom_cursor_);
+ } else {
+ HINSTANCE mod_handle = GetModuleHandle(NULL);
+ host->SetCursor(cursor.GetCursor(mod_handle));
+ }
+ }
+}
+
+void TestWebViewDelegate::GetWindowLocation(WebWidget* webwidget,
+ gfx::Point* origin) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget)) {
+ RECT rect;
+ GetWindowRect(host->window_handle(), &rect);
+ origin->SetPoint(rect.left, rect.top);
+ }
+}
+
+void TestWebViewDelegate::SetWindowRect(WebWidget* webwidget,
+ const gfx::Rect& rect) {
+ if (webwidget == shell_->webView()) {
+ // ignored
+ } else if (webwidget == shell_->popup()) {
+ MoveWindow(shell_->popupWnd(),
+ rect.x(), rect.y(), rect.width(), rect.height(), FALSE);
+ }
+}
+
+void TestWebViewDelegate::DidMove(WebWidget* webwidget,
+ const WebPluginGeometry& move) {
+ WebPluginDelegateImpl::MoveWindow(
+ move.window, move.window_rect, move.clip_rect, move.visible);
+}
+
+void TestWebViewDelegate::RunModal(WebWidget* webwidget) {
+ Show(webwidget, NEW_WINDOW);
+
+ WindowList* wl = TestShell::windowList();
+ for (WindowList::const_iterator i = wl->begin(); i != wl->end(); ++i) {
+ if (*i != shell_->mainWnd())
+ EnableWindow(*i, FALSE);
+ }
+
+ shell_->set_is_modal(true);
+ MessageLoop::current()->Run();
+
+ for (WindowList::const_iterator i = wl->begin(); i != wl->end(); ++i)
+ EnableWindow(*i, TRUE);
+}
+
+void TestWebViewDelegate::RegisterDragDrop() {
+ DCHECK(!drop_delegate_);
+ drop_delegate_ = new TestDropDelegate(shell_->webViewWnd(),
+ shell_->webView());
+}
+
+// Private methods -----------------------------------------------------------
+
+void TestWebViewDelegate::UpdateAddressBar(WebView* webView) {
+ WebFrame* mainFrame = webView->GetMainFrame();
+
+ WebDataSource* dataSource = mainFrame->GetDataSource();
+ if (!dataSource)
+ dataSource = mainFrame->GetProvisionalDataSource();
+ if (!dataSource)
+ return;
+
+ std::wstring frameURL =
+ UTF8ToWide(dataSource->GetRequest().GetMainDocumentURL().spec());
+ SendMessage(shell_->editWnd(), WM_SETTEXT, 0,
+ reinterpret_cast<LPARAM>(frameURL.c_str()));
+}
+
+void TestWebViewDelegate::LocationChangeDone(WebDataSource* data_source) {
+ if (data_source->GetWebFrame() == top_loading_frame_) {
+ top_loading_frame_ = NULL;
+
+ if (!shell_->interactive())
+ shell_->layout_test_controller()->LocationChangeDone();
+ }
+}
+
+WebWidgetHost* TestWebViewDelegate::GetHostForWidget(WebWidget* webwidget) {
+ if (webwidget == shell_->webView())
+ return shell_->webViewHost();
+ if (webwidget == shell_->popup())
+ return shell_->popupHost();
+ return NULL;
+}
+
+void TestWebViewDelegate::UpdateForCommittedLoad(WebFrame* frame,
+ bool is_new_navigation) {
+ WebView* webview = shell_->webView();
+
+ // Code duplicated from RenderView::DidCommitLoadForFrame.
+ const WebRequest& request =
+ webview->GetMainFrame()->GetDataSource()->GetRequest();
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(request.GetExtraData());
+
+ if (is_new_navigation) {
+ // New navigation.
+ UpdateSessionHistory(frame);
+ page_id_ = next_page_id_++;
+ } else if (extra_data && extra_data->pending_page_id != -1 &&
+ !extra_data->request_committed) {
+ // This is a successful session history navigation!
+ UpdateSessionHistory(frame);
+ page_id_ = extra_data->pending_page_id;
+ }
+
+ // Don't update session history multiple times.
+ if (extra_data)
+ extra_data->request_committed = true;
+
+ UpdateURL(frame);
+}
+
+void TestWebViewDelegate::UpdateURL(WebFrame* frame) {
+ WebDataSource* ds = frame->GetDataSource();
+ DCHECK(ds);
+
+ const WebRequest& request = ds->GetRequest();
+
+ // We don't hold a reference to the extra data. The request's reference will
+ // be sufficient because we won't modify it during our call. MAY BE NULL.
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(request.GetExtraData());
+
+ // Type is unused.
+ scoped_ptr<TestNavigationEntry> entry(new TestNavigationEntry);
+
+ // Bug 654101: the referrer will be empty on https->http transitions. It
+ // would be nice if we could get the real referrer from somewhere.
+ entry->SetPageID(page_id_);
+ if (ds->HasUnreachableURL()) {
+ entry->SetURL(GURL(ds->GetUnreachableURL()));
+ } else {
+ entry->SetURL(GURL(request.GetURL()));
+ }
+
+ if (shell_->webView()->GetMainFrame() == frame) {
+ // Top-level navigation.
+
+ PageTransition::Type transition = extra_data ?
+ extra_data->transition_type : PageTransition::LINK;
+ if (!PageTransition::IsMainFrame(transition)) {
+ transition = PageTransition::LINK;
+ }
+ entry->SetTransition(transition);
+ } else {
+ PageTransition::Type transition;
+ if (page_id_ > last_page_id_updated_)
+ transition = PageTransition::MANUAL_SUBFRAME;
+ else
+ transition = PageTransition::AUTO_SUBFRAME;
+ entry->SetTransition(transition);
+ }
+
+ shell_->navigation_controller()->DidNavigateToEntry(entry.release());
+
+ last_page_id_updated_ = std::max(last_page_id_updated_, page_id_);
+}
+
+void TestWebViewDelegate::UpdateSessionHistory(WebFrame* frame) {
+ // If we have a valid page ID at this point, then it corresponds to the page
+ // we are navigating away from. Otherwise, this is the first navigation, so
+ // there is no past session history to record.
+ if (page_id_ == -1)
+ return;
+
+ TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+ shell_->navigation_controller()->GetEntryWithPageID(
+ TestNavigationEntry::GetTabContentsType(), page_id_));
+ if (!entry)
+ return;
+
+ GURL url;
+ std::wstring title;
+ std::string state;
+ if (!shell_->webView()->GetMainFrame()->
+ GetPreviousState(&url, &title, &state))
+ return;
+
+ entry->SetURL(url);
+ entry->SetTitle(title);
+ entry->SetContentState(state);
+}
+
+std::wstring TestWebViewDelegate::GetFrameDescription(WebFrame* webframe) {
+ std::wstring name = webframe->GetName();
+
+ if (webframe == shell_->webView()->GetMainFrame()) {
+ if (name.length())
+ return L"main frame \"" + name + L"\"";
+ else
+ return L"main frame";
+ } else {
+ if (name.length())
+ return L"frame \"" + name + L"\"";
+ else
+ return L"frame (anonymous)";
+ }
+}
diff --git a/webkit/tools/test_shell/test_webview_delegate.h b/webkit/tools/test_shell/test_webview_delegate.h
new file mode 100644
index 0000000..9a6f5e7
--- /dev/null
+++ b/webkit/tools/test_shell/test_webview_delegate.h
@@ -0,0 +1,303 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// TestWebViewDelegate class:
+// This class implements the WebViewDelegate methods for the test shell. One
+// instance is owned by each TestShell.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
+
+#include <windows.h>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "webkit/glue/webview_delegate.h"
+#include "webkit/glue/webwidget_delegate.h"
+#include "webkit/tools/test_shell/drag_delegate.h"
+#include "webkit/tools/test_shell/drop_delegate.h"
+
+struct WebPreferences;
+class GURL;
+class TestShell;
+class WebDataSource;
+class WebWidgetHost;
+
+class TestWebViewDelegate : public base::RefCounted<TestWebViewDelegate>, public WebViewDelegate {
+ public:
+ struct CapturedContextMenuEvent {
+ CapturedContextMenuEvent(ContextNode::Type in_type,
+ int in_x,
+ int in_y)
+ : type(in_type),
+ x(in_x),
+ y(in_y) {
+ }
+
+ ContextNode::Type type;
+ int x;
+ int y;
+ };
+
+ typedef std::vector<CapturedContextMenuEvent> CapturedContextMenuEvents;
+
+ TestWebViewDelegate(TestShell* shell)
+ : shell_(shell),
+ top_loading_frame_(NULL),
+ page_id_(-1),
+ last_page_id_updated_(-1),
+ page_is_loading_(false),
+ is_custom_policy_delegate_(false),
+ custom_cursor_(NULL) {
+ }
+ virtual ~TestWebViewDelegate();
+
+ // WebViewDelegate
+ virtual WebView* CreateWebView(WebView* webview, bool user_gesture);
+ virtual WebWidget* CreatePopupWidget(WebView* webview);
+ virtual WebPluginDelegate* CreatePluginDelegate(
+ WebView* webview,
+ const GURL& url,
+ const std::string& mime_type,
+ const std::string& clsid,
+ std::string* actual_mime_type);
+ virtual void OpenURL(WebView* webview,
+ const GURL& url,
+ WindowOpenDisposition disposition);
+ virtual void RunJavaScriptAlert(WebView* webview,
+ const std::wstring& message);
+ virtual bool RunJavaScriptConfirm(WebView* webview,
+ const std::wstring& message);
+ virtual bool RunJavaScriptPrompt(WebView* webview,
+ const std::wstring& message,
+ const std::wstring& default_value,
+ std::wstring* result);
+ virtual void AddMessageToConsole(WebView* webview,
+ const std::wstring& message,
+ unsigned int line_no,
+ const std::wstring& source_id);
+ virtual void StartDragging(WebView* webview,
+ const WebDropData& drop_data);
+ virtual void ShowContextMenu(WebView* webview,
+ ContextNode::Type type,
+ int x,
+ int y,
+ const GURL& link_url,
+ const GURL& image_url,
+ const GURL& page_url,
+ const GURL& frame_url,
+ const std::wstring& selection_text,
+ const std::wstring& misspelled_word,
+ int edit_flags);
+ virtual void DidStartProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame,
+ NavigationGesture gesture);
+ virtual void DidReceiveServerRedirectForProvisionalLoadForFrame(
+ WebView* webview, WebFrame* frame);
+ virtual void DidFailProvisionalLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* frame);
+ virtual void DidCommitLoadForFrame(WebView* webview, WebFrame* frame,
+ bool is_new_navigation);
+ virtual void DidReceiveTitle(WebView* webview,
+ const std::wstring& title,
+ WebFrame* frame);
+ virtual void DidFinishDocumentLoadForFrame(WebView* webview,
+ WebFrame* frame);
+ virtual void DidHandleOnloadEventsForFrame(WebView* webview,
+ WebFrame* frame);
+ virtual void DidChangeLocationWithinPageForFrame(WebView* webview,
+ WebFrame* frame,
+ bool is_new_navigation);
+ virtual void DidReceiveIconForFrame(WebView* webview, WebFrame* frame);
+
+ virtual void WillPerformClientRedirect(WebView* webview,
+ WebFrame* frame,
+ const std::wstring& dest_url,
+ unsigned int delay_seconds,
+ unsigned int fire_date);
+ virtual void DidCancelClientRedirect(WebView* webview,
+ WebFrame* frame);
+
+ virtual void DidFinishLoadForFrame(WebView* webview, WebFrame* frame);
+ virtual void DidFailLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* forFrame);
+
+ virtual void AssignIdentifierToRequest(WebView* webview,
+ uint32 identifier,
+ const WebRequest& request);
+ virtual void WillSendRequest(WebView* webview,
+ uint32 identifier,
+ WebRequest* request);
+ virtual void DidFinishLoading(WebView* webview, uint32 identifier);
+ virtual void DidFailLoadingWithError(WebView* webview,
+ uint32 identifier,
+ const WebError& error);
+
+ virtual bool ShouldBeginEditing(WebView* webview, std::wstring range);
+ virtual bool ShouldEndEditing(WebView* webview, std::wstring range);
+ virtual bool ShouldInsertNode(WebView* webview,
+ std::wstring node,
+ std::wstring range,
+ std::wstring action);
+ virtual bool ShouldInsertText(WebView* webview,
+ std::wstring text,
+ std::wstring range,
+ std::wstring action);
+ virtual bool ShouldChangeSelectedRange(WebView* webview,
+ std::wstring fromRange,
+ std::wstring toRange,
+ std::wstring affinity,
+ bool stillSelecting);
+ virtual bool ShouldDeleteRange(WebView* webview, std::wstring range);
+ virtual bool ShouldApplyStyle(WebView* webview,
+ std::wstring style,
+ std::wstring range);
+ virtual bool SmartInsertDeleteEnabled();
+ virtual void DidBeginEditing();
+ virtual void DidChangeSelection();
+ virtual void DidChangeContents();
+ virtual void DidEndEditing();
+
+ virtual void DidStartLoading(WebView* webview);
+ virtual void DidStopLoading(WebView* webview);
+
+ virtual void WindowObjectCleared(WebFrame* webframe);
+ virtual WindowOpenDisposition DispositionForNavigationAction(
+ WebView* webview,
+ WebFrame* frame,
+ const WebRequest* request,
+ WebNavigationType type,
+ WindowOpenDisposition disposition,
+ bool is_redirect);
+ void TestWebViewDelegate::SetCustomPolicyDelegate(bool isCustom);
+ virtual WebHistoryItem* GetHistoryEntryAtOffset(int offset);
+ virtual void GoToEntryAtOffsetAsync(int offset);
+ virtual int GetHistoryBackListCount();
+ virtual int GetHistoryForwardListCount();
+
+ // WebWidgetDelegate
+ virtual HWND GetContainingWindow(WebWidget* webwidget);
+ virtual void DidInvalidateRect(WebWidget* webwidget, const gfx::Rect& rect);
+ virtual void DidScrollRect(WebWidget* webwidget, int dx, int dy,
+ const gfx::Rect& clip_rect);
+ virtual void Show(WebWidget* webview, WindowOpenDisposition disposition);
+ virtual void CloseWidgetSoon(WebWidget* webwidget);
+ virtual void Focus(WebWidget* webwidget);
+ virtual void Blur(WebWidget* webwidget);
+ virtual void SetCursor(WebWidget* webwidget,
+ const WebCursor& cursor);
+ virtual void GetWindowLocation(WebWidget* webwidget, gfx::Point* origin);
+ virtual void SetWindowRect(WebWidget* webwidget, const gfx::Rect& rect);
+ virtual void DidMove(WebWidget* webwidget, const WebPluginGeometry& move);
+ virtual void RunModal(WebWidget* webwidget);
+ virtual void AddRef() {
+ RefCounted<TestWebViewDelegate>::AddRef();
+ }
+ virtual void Release() {
+ RefCounted<TestWebViewDelegate>::Release();
+ }
+
+ // Additional accessors
+ WebFrame* top_loading_frame() { return top_loading_frame_; }
+ IDropTarget* drop_delegate() { return drop_delegate_.get(); }
+ IDropSource* drag_delegate() { return drag_delegate_.get(); }
+ const CapturedContextMenuEvents& captured_context_menu_events() const {
+ return captured_context_menu_events_;
+ }
+ void clear_captured_context_menu_events() {
+ captured_context_menu_events_.clear();
+ }
+
+ // Methods for modifying WebPreferences
+ void SetUserStyleSheetEnabled(bool is_enabled);
+ void SetUserStyleSheetLocation(const GURL& location);
+ void SetDashboardCompatibilityMode(bool use_mode);
+
+ // Sets the webview as a drop target.
+ void RegisterDragDrop();
+
+ protected:
+ void UpdateAddressBar(WebView* webView);
+
+ // In the Mac code, this is called to trigger the end of a test after the
+ // page has finished loading. From here, we can generate the dump for the
+ // test.
+ void LocationChangeDone(WebDataSource* data_source);
+
+ WebWidgetHost* GetHostForWidget(WebWidget* webwidget);
+
+ void UpdateForCommittedLoad(WebFrame* webframe, bool is_new_navigation);
+ void UpdateURL(WebFrame* frame);
+ void UpdateSessionHistory(WebFrame* frame);
+
+ // Get a string suitable for dumping a frame to the console.
+ std::wstring GetFrameDescription(WebFrame* webframe);
+
+ private:
+ // True while a page is in the process of being loaded. This flag should
+ // not be necessary, but it helps guard against mismatched messages for
+ // starting and ending loading frames.
+ bool page_is_loading_;
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ bool is_custom_policy_delegate_;
+
+ // Non-owning pointer. The delegate is owned by the host.
+ TestShell* shell_;
+
+ // This is non-NULL IFF a load is in progress.
+ WebFrame* top_loading_frame_;
+
+ // For tracking session history. See RenderView.
+ int page_id_;
+ int last_page_id_updated_;
+
+ // Maps resource identifiers to a descriptive string.
+ typedef std::map<uint32, std::string> ResourceMap;
+ ResourceMap resource_identifier_map_;
+ std::string GetResourceDescription(uint32 identifier);
+
+ HCURSOR custom_cursor_;
+
+ // Classes needed by drag and drop.
+ scoped_refptr<TestDragDelegate> drag_delegate_;
+ scoped_refptr<TestDropDelegate> drop_delegate_;
+
+ CapturedContextMenuEvents captured_context_menu_events_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestWebViewDelegate);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
diff --git a/webkit/tools/test_shell/text_input_controller.cc b/webkit/tools/test_shell/text_input_controller.cc
new file mode 100644
index 0000000..d74346b
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller.cc
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/text_input_controller.h"
+
+#include "webkit/glue/webview.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webtextinput.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+TestShell* TextInputController::shell_ = NULL;
+
+TextInputController::TextInputController(TestShell* shell) {
+ // Set static shell_ variable. Be careful not to assign shell_ to new
+ // windows which are temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ BindMethod("insertText", &TextInputController::insertText);
+ BindMethod("doCommand", &TextInputController::doCommand);
+ BindMethod("setMarkedText", &TextInputController::setMarkedText);
+ BindMethod("unmarkText", &TextInputController::unmarkText);
+ BindMethod("hasMarkedText", &TextInputController::hasMarkedText);
+ BindMethod("conversationIdentifier", &TextInputController::conversationIdentifier);
+ BindMethod("substringFromRange", &TextInputController::substringFromRange);
+ BindMethod("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange);
+ BindMethod("markedRange", &TextInputController::markedRange);
+ BindMethod("selectedRange", &TextInputController::selectedRange);
+ BindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange);
+ BindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint);
+ BindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText);
+ BindMethod("makeAttributedString", &TextInputController::makeAttributedString);
+}
+
+/* static */ WebView* TextInputController::webview() {
+ return shell_->webView();
+}
+
+/* static */ WebTextInput* TextInputController::GetTextInput() {
+ return webview()->GetMainFrame()->GetTextInput();
+}
+
+void TextInputController::insertText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->InsertText(args[0].ToString());
+ }
+}
+
+void TextInputController::doCommand(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->DoCommand(args[0].ToString());
+ }
+}
+
+void TextInputController::setMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 3 && args[0].isString()
+ && args[1].isNumber() && args[2].isNumber()) {
+ text_input->SetMarkedText(args[0].ToString(),
+ args[1].ToInt32(),
+ args[2].ToInt32());
+ }
+}
+
+void TextInputController::unmarkText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ text_input->UnMarkText();
+}
+
+void TextInputController::hasMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ result->Set(text_input->HasMarkedText());
+}
+
+void TextInputController::conversationIdentifier(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ text_input->ConversationIdentifier();
+}
+
+void TextInputController::substringFromRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->SubstringFromRange(args[0].ToInt32(), args[1].ToInt32());
+ }
+}
+
+void TextInputController::attributedSubstringFromRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->AttributedSubstringFromRange(args[0].ToInt32(),
+ args[1].ToInt32());
+ }
+}
+
+void TextInputController::markedRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string range_str;
+ text_input->MarkedRange(&range_str);
+ result->Set(range_str);
+}
+
+void TextInputController::selectedRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string range_str;
+ text_input->SelectedRange(&range_str);
+ result->Set(range_str);
+}
+
+void TextInputController::firstRectForCharacterRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->FirstRectForCharacterRange(args[0].ToInt32(),
+ args[1].ToInt32());
+ }
+}
+
+void TextInputController::characterIndexForPoint(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isDouble() && args[1].isDouble()) {
+ text_input->CharacterIndexForPoint(args[0].ToDouble(),
+ args[1].ToDouble());
+ }
+}
+
+void TextInputController::validAttributesForMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string attributes_str;
+ text_input->ValidAttributesForMarkedText(&attributes_str);
+ result->Set(attributes_str);
+}
+
+void TextInputController::makeAttributedString(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->MakeAttributedString(args[0].ToString());
+ }
+}
diff --git a/webkit/tools/test_shell/text_input_controller.h b/webkit/tools/test_shell/text_input_controller.h
new file mode 100644
index 0000000..f54a307
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller.h
@@ -0,0 +1,74 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// TextInputController is bound to window.textInputController in Javascript
+// when test_shell is running noninteractively. Layout tests use it to
+// exercise various corners of text input.
+//
+// Mac equivalent: WebKit/WebKitTools/DumpRenderTree/TextInputController.{h,m}
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
+
+#include "webkit/glue/cpp_bound_class.h"
+
+class TestShell;
+class WebView;
+class WebTextInput;
+
+class TextInputController : public CppBoundClass {
+ public:
+ TextInputController(TestShell* shell);
+
+ void insertText(const CppArgumentList& args, CppVariant* result);
+ void doCommand(const CppArgumentList& args, CppVariant* result);
+ void setMarkedText(const CppArgumentList& args, CppVariant* result);
+ void unmarkText(const CppArgumentList& args, CppVariant* result);
+ void hasMarkedText(const CppArgumentList& args, CppVariant* result);
+ void conversationIdentifier(const CppArgumentList& args, CppVariant* result);
+ void substringFromRange(const CppArgumentList& args, CppVariant* result);
+ void attributedSubstringFromRange(const CppArgumentList& args, CppVariant* result);
+ void markedRange(const CppArgumentList& args, CppVariant* result);
+ void selectedRange(const CppArgumentList& args, CppVariant* result);
+ void firstRectForCharacterRange(const CppArgumentList& args, CppVariant* result);
+ void characterIndexForPoint(const CppArgumentList& args, CppVariant* result);
+ void validAttributesForMarkedText(const CppArgumentList& args, CppVariant* result);
+ void makeAttributedString(const CppArgumentList& args, CppVariant* result);
+
+ private:
+ static WebTextInput* GetTextInput();
+
+ // Returns the test shell's webview.
+ static WebView* webview();
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/text_input_controller_unittest.cc b/webkit/tools/test_shell/text_input_controller_unittest.cc
new file mode 100644
index 0000000..1e2da90
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/text_input_controller.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test class to let us check TextInputController's method table.
+class TestTextInputController : public TextInputController {
+ public:
+ TestTextInputController() : TextInputController(NULL) {}
+
+ size_t MethodCount() {
+ return methods_.size();
+ }
+};
+
+
+TEST(TextInputControllerTest, MethodMapIsInitialized) {
+ TestTextInputController text_input_controller;
+
+ EXPECT_EQ(14, text_input_controller.MethodCount());
+
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "insertText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "doCommand"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "setMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "unmarkText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "hasMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "conversationIdentifier"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "substringFromRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "attributedSubstringFromRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "markedRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "selectedRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "firstRectForCharacterRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "characterIndexForPoint"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "validAttributesForMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "makeAttributedString"));
+
+ // Negative test.
+ EXPECT_FALSE(text_input_controller.IsMethodRegistered(
+ "momeRathsOutgrabe"));
+}
diff --git a/webkit/tools/test_shell/webview_host.cc b/webkit/tools/test_shell/webview_host.cc
new file mode 100644
index 0000000..61f66f0
--- /dev/null
+++ b/webkit/tools/test_shell/webview_host.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/webview_host.h"
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/win_util.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webview.h"
+
+static const wchar_t kWindowClassName[] = L"WebViewHost";
+
+/*static*/
+WebViewHost* WebViewHost::Create(HWND parent_window, WebViewDelegate* delegate,
+ const WebPreferences& prefs) {
+ WebViewHost* host = new WebViewHost();
+
+ static bool registered_class = false;
+ if (!registered_class) {
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = WebWidgetHost::WndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.lpszClassName = kWindowClassName;
+ RegisterClassEx(&wcex);
+ registered_class = true;
+ }
+
+ host->hwnd_ = CreateWindow(kWindowClassName, NULL,
+ WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS, 0, 0,
+ 0, 0, parent_window, NULL,
+ GetModuleHandle(NULL), NULL);
+ win_util::SetWindowUserData(host->hwnd_, host);
+
+ host->webwidget_ = WebView::Create(delegate, prefs);
+
+ return host;
+}
+
+WebView* WebViewHost::webview() const {
+ return static_cast<WebView*>(webwidget_);
+}
diff --git a/webkit/tools/test_shell/webview_host.h b/webkit/tools/test_shell/webview_host.h
new file mode 100644
index 0000000..e2f84d6
--- /dev/null
+++ b/webkit/tools/test_shell/webview_host.h
@@ -0,0 +1,61 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
+
+#include <windows.h>
+
+#include "base/gfx/rect.h"
+#include "base/scoped_ptr.h"
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+struct WebPreferences;
+class WebView;
+class WebViewDelegate;
+
+// This class is a simple HWND-based host for a WebView
+class WebViewHost : public WebWidgetHost {
+ public:
+ // The new instance is deleted once the associated HWND is destroyed. The
+ // newly created window should be resized after it is created, using the
+ // MoveWindow (or equivalent) function.
+ static WebViewHost* Create(HWND parent_window,
+ WebViewDelegate* delegate,
+ const WebPreferences& prefs);
+
+ WebView* webview() const;
+
+ protected:
+ virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
+ return false;
+ }
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
diff --git a/webkit/tools/test_shell/webwidget_host.cc b/webkit/tools/test_shell/webwidget_host.cc
new file mode 100644
index 0000000..b037ca9
--- /dev/null
+++ b/webkit/tools/test_shell/webwidget_host.cc
@@ -0,0 +1,354 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/win_util.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webwidget.h"
+
+static const wchar_t kWindowClassName[] = L"WebWidgetHost";
+
+/*static*/
+WebWidgetHost* WebWidgetHost::Create(HWND parent_window, WebWidgetDelegate* delegate) {
+ WebWidgetHost* host = new WebWidgetHost();
+
+ static bool registered_class = false;
+ if (!registered_class) {
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = WebWidgetHost::WndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.lpszClassName = kWindowClassName;
+ RegisterClassEx(&wcex);
+ registered_class = true;
+ }
+
+ host->hwnd_ = CreateWindowEx(WS_EX_TOOLWINDOW,
+ kWindowClassName, kWindowClassName, WS_POPUP,
+ 0, 0, 0, 0,
+ parent_window, NULL, GetModuleHandle(NULL), NULL);
+
+ win_util::SetWindowUserData(host->hwnd_, host);
+
+ host->webwidget_ = WebWidget::Create(delegate);
+
+ return host;
+}
+
+/*static*/
+WebWidgetHost* WebWidgetHost::FromWindow(HWND hwnd) {
+ return reinterpret_cast<WebWidgetHost*>(win_util::GetWindowUserData(hwnd));
+}
+
+/*static*/
+LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam,
+ LPARAM lparam) {
+ WebWidgetHost* host = FromWindow(hwnd);
+ if (host && !host->WndProc(message, wparam, lparam)) {
+ switch (message) {
+ case WM_DESTROY:
+ delete host;
+ break;
+
+ case WM_PAINT:
+ host->Paint();
+ return 0;
+
+ case WM_ERASEBKGND:
+ // Do nothing here to avoid flashing, the background will be erased
+ // during painting.
+ return 0;
+
+ case WM_SIZE:
+ host->Resize(lparam);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ case WM_MOUSELEAVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ host->MouseEvent(message, wparam, lparam);
+ break;
+
+ case WM_MOUSEWHEEL:
+ host->WheelEvent(wparam, lparam);
+ break;
+
+ case WM_CAPTURECHANGED:
+ case WM_CANCELMODE:
+ host->CaptureLostEvent();
+ break;
+
+ // TODO(darin): add WM_SYSKEY{DOWN/UP} to capture ALT key actions
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_IME_CHAR:
+ host->KeyEvent(message, wparam, lparam);
+ break;
+
+ case WM_SETFOCUS:
+ host->SetFocus(true);
+ break;
+
+ case WM_KILLFOCUS:
+ host->SetFocus(false);
+ break;
+ }
+ }
+
+ return DefWindowProc(hwnd, message, wparam, lparam);;
+}
+
+void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
+ DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
+
+ // If this invalidate overlaps with a pending scroll, then we have to
+ // downgrade to invalidating the scroll rect.
+ if (damaged_rect.Intersects(scroll_rect_)) {
+ paint_rect_ = paint_rect_.Union(scroll_rect_);
+ ResetScrollRect();
+ }
+ paint_rect_ = paint_rect_.Union(damaged_rect);
+
+ RECT r = damaged_rect.ToRECT();
+ InvalidateRect(hwnd_, &r, FALSE);
+}
+
+void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
+ DCHECK(dx || dy);
+
+ // If we already have a pending scroll operation or if this scroll operation
+ // intersects the existing paint region, then just failover to invalidating.
+ if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
+ paint_rect_ = paint_rect_.Union(scroll_rect_);
+ ResetScrollRect();
+ paint_rect_ = paint_rect_.Union(clip_rect);
+ }
+
+ // We will perform scrolling lazily, when requested to actually paint.
+ scroll_rect_ = clip_rect;
+ scroll_dx_ = dx;
+ scroll_dy_ = dy;
+
+ RECT r = clip_rect.ToRECT();
+ InvalidateRect(hwnd_, &r, FALSE);
+}
+
+void WebWidgetHost::SetCursor(HCURSOR cursor) {
+ SetClassLong(hwnd_, GCL_HCURSOR,
+ static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
+ ::SetCursor(cursor);
+}
+
+void WebWidgetHost::DiscardBackingStore() {
+ canvas_.reset();
+}
+
+WebWidgetHost::WebWidgetHost()
+ : hwnd_(NULL),
+ webwidget_(NULL),
+ track_mouse_leave_(false),
+ scroll_dx_(0),
+ scroll_dy_(0) {
+ set_painting(false);
+}
+
+WebWidgetHost::~WebWidgetHost() {
+ win_util::SetWindowUserData(hwnd_, 0);
+
+ TrackMouseLeave(false);
+
+ webwidget_->Close();
+ webwidget_->Release();
+}
+
+bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case WM_ACTIVATE:
+ if (wparam == WA_INACTIVE) {
+ PostMessage(hwnd_, WM_CLOSE, 0, 0);
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+void WebWidgetHost::Paint() {
+ RECT r;
+ GetClientRect(hwnd_, &r);
+ gfx::Rect client_rect(r);
+
+ // Allocate a canvas if necessary
+ if (!canvas_.get()) {
+ ResetScrollRect();
+ paint_rect_ = client_rect;
+ canvas_.reset(new gfx::PlatformCanvas(
+ paint_rect_.width(), paint_rect_.height(), true));
+ }
+
+ // This may result in more invalidation
+ webwidget_->Layout();
+
+ // Scroll the canvas if necessary
+ scroll_rect_ = client_rect.Intersect(scroll_rect_);
+ if (!scroll_rect_.IsEmpty()) {
+ HDC hdc = canvas_->getTopPlatformDevice().getBitmapDC();
+
+ RECT damaged_rect, r = scroll_rect_.ToRECT();
+ ScrollDC(hdc, scroll_dx_, scroll_dy_, NULL, &r, NULL, &damaged_rect);
+
+ PaintRect(gfx::Rect(damaged_rect));
+ }
+ ResetScrollRect();
+
+ // Paint the canvas if necessary. Allow painting to generate extra rects the
+ // first time we call it. This is necessary because some WebCore rendering
+ // objects update their layout only when painted.
+ for (int i = 0; i < 2; ++i) {
+ paint_rect_ = client_rect.Intersect(paint_rect_);
+ if (!paint_rect_.IsEmpty()) {
+ gfx::Rect rect(paint_rect_);
+ paint_rect_ = gfx::Rect();
+
+ DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
+ PaintRect(rect);
+ }
+ }
+ DCHECK(paint_rect_.IsEmpty());
+
+ // Paint to the screen
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd_, &ps);
+ canvas_->getTopPlatformDevice().drawToHDC(ps.hdc,
+ ps.rcPaint.left,
+ ps.rcPaint.top,
+ &ps.rcPaint);
+ EndPaint(hwnd_, &ps);
+
+ // Draw children
+ UpdateWindow(hwnd_);
+}
+
+void WebWidgetHost::Resize(LPARAM lparam) {
+ // Force an entire re-paint. TODO(darin): Maybe reuse this memory buffer.
+ DiscardBackingStore();
+
+ webwidget_->Resize(gfx::Size(LOWORD(lparam), HIWORD(lparam)));
+}
+
+void WebWidgetHost::MouseEvent(UINT message, WPARAM wparam, LPARAM lparam) {
+ WebMouseEvent event(hwnd_, message, wparam, lparam);
+ switch (event.type) {
+ case WebInputEvent::MOUSE_MOVE:
+ TrackMouseLeave(true);
+ break;
+ case WebInputEvent::MOUSE_LEAVE:
+ TrackMouseLeave(false);
+ break;
+ case WebInputEvent::MOUSE_DOWN:
+ SetCapture(hwnd_);
+ break;
+ case WebInputEvent::MOUSE_UP:
+ if (GetCapture() == hwnd_)
+ ReleaseCapture();
+ break;
+ }
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::WheelEvent(WPARAM wparam, LPARAM lparam) {
+ WebMouseWheelEvent event(hwnd_, WM_MOUSEWHEEL, wparam, lparam);
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::KeyEvent(UINT message, WPARAM wparam, LPARAM lparam) {
+ WebKeyboardEvent event(hwnd_, message, wparam, lparam);
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::CaptureLostEvent() {
+ webwidget_->MouseCaptureLost();
+}
+
+void WebWidgetHost::SetFocus(bool enable) {
+ webwidget_->SetFocus(enable);
+}
+
+void WebWidgetHost::TrackMouseLeave(bool track) {
+ if (track == track_mouse_leave_)
+ return;
+ track_mouse_leave_ = track;
+
+ DCHECK(hwnd_);
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ if (!track_mouse_leave_)
+ tme.dwFlags |= TME_CANCEL;
+ tme.hwndTrack = hwnd_;
+
+ TrackMouseEvent(&tme);
+}
+
+void WebWidgetHost::ResetScrollRect() {
+ scroll_rect_ = gfx::Rect();
+ scroll_dx_ = 0;
+ scroll_dy_ = 0;
+}
+
+void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
+#ifndef NDEBUG
+ DCHECK(!painting_);
+#endif
+ DCHECK(canvas_.get());
+
+ set_painting(true);
+ webwidget_->Paint(canvas_.get(), rect);
+ set_painting(false);
+}
diff --git a/webkit/tools/test_shell/webwidget_host.h b/webkit/tools/test_shell/webwidget_host.h
new file mode 100644
index 0000000..e0dde64
--- /dev/null
+++ b/webkit/tools/test_shell/webwidget_host.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__
+
+#include <windows.h>
+
+#include "base/gfx/rect.h"
+#include "base/scoped_ptr.h"
+
+class WebWidget;
+class WebWidgetDelegate;
+
+namespace gfx {
+class PlatformCanvas;
+class Size;
+}
+
+// This class is a simple HWND-based host for a WebWidget
+class WebWidgetHost {
+ public:
+ // The new instance is deleted once the associated HWND is destroyed. The
+ // newly created window should be resized after it is created, using the
+ // MoveWindow (or equivalent) function.
+ static WebWidgetHost* Create(HWND parent_window, WebWidgetDelegate* delegate);
+
+ static WebWidgetHost* FromWindow(HWND hwnd);
+
+ HWND window_handle() const { return hwnd_; }
+ WebWidget* webwidget() const { return webwidget_; }
+
+ void DidInvalidateRect(const gfx::Rect& rect);
+ void DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect);
+ void SetCursor(HCURSOR cursor);
+
+ void DiscardBackingStore();
+
+ protected:
+ WebWidgetHost();
+ ~WebWidgetHost();
+
+ // Per-class wndproc. Returns true if the event should be swallowed.
+ virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam);
+
+ void Paint();
+ void Resize(LPARAM lparam);
+ void MouseEvent(UINT message, WPARAM wparam, LPARAM lparam);
+ void WheelEvent(WPARAM wparam, LPARAM lparam);
+ void KeyEvent(UINT message, WPARAM wparam, LPARAM lparam);
+ void CaptureLostEvent();
+ void SetFocus(bool enable);
+
+ void TrackMouseLeave(bool enable);
+ void ResetScrollRect();
+ void PaintRect(const gfx::Rect& rect);
+
+ void set_painting(bool value) {
+#ifndef NDEBUG
+ painting_ = value;
+#endif
+ }
+
+ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+ HWND hwnd_;
+ WebWidget* webwidget_;
+ scoped_ptr<gfx::PlatformCanvas> canvas_;
+
+ // specifies the portion of the webwidget that needs painting
+ gfx::Rect paint_rect_;
+
+ // specifies the portion of the webwidget that needs scrolling
+ gfx::Rect scroll_rect_;
+ int scroll_dx_;
+ int scroll_dy_;
+
+ bool track_mouse_leave_;
+
+#ifndef NDEBUG
+ bool painting_;
+#endif
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__