Merge from Chromium at DEPS revision r213371

This commit was generated by merge_to_master.py.

Change-Id: I03fae1c1dae6e5de12e56e0a6c3780252291bfae
diff --git a/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.cpp b/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.cpp
index 0cd4164..1b2b9ff 100644
--- a/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.cpp
+++ b/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.cpp
@@ -258,8 +258,6 @@
     bindMethod("setPrinting", &TestRunner::setPrinting);
     bindMethod("setShouldStayOnPageAfterHandlingBeforeUnload", &TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload);
     bindMethod("setWillSendRequestClearHeader", &TestRunner::setWillSendRequestClearHeader);
-    bindMethod("setWillSendRequestReturnsNull", &TestRunner::setWillSendRequestReturnsNull);
-    bindMethod("setWillSendRequestReturnsNullOnRedirect", &TestRunner::setWillSendRequestReturnsNullOnRedirect);
     bindMethod("dumpResourceRequestPriorities", &TestRunner::dumpResourceRequestPriorities);
 
     // The following methods interact with the WebTestProxy.
@@ -419,8 +417,6 @@
     m_sweepHorizontally = false;
     m_isPrinting = false;
     m_shouldStayOnPageAfterHandlingBeforeUnload = false;
-    m_shouldBlockRedirects = false;
-    m_willSendRequestShouldReturnNull = false;
     m_shouldDumpResourcePriorities = false;
 
     m_httpHeadersToClear.clear();
@@ -628,16 +624,6 @@
     return &m_httpHeadersToClear;
 }
 
-bool TestRunner::shouldBlockRedirects() const
-{
-    return m_shouldBlockRedirects;
-}
-
-bool TestRunner::willSendRequestShouldReturnNull() const
-{
-    return m_willSendRequestShouldReturnNull;
-}
-
 void TestRunner::setTopLoadingFrame(WebFrame* frame, bool clear)
 {
     if (frame->top()->view() != m_webView)
@@ -1111,20 +1097,6 @@
     result->setNull();
 }
 
-void TestRunner::setWillSendRequestReturnsNullOnRedirect(const CppArgumentList& arguments, CppVariant* result)
-{
-    if (arguments.size() > 0 && arguments[0].isBool())
-        m_shouldBlockRedirects = arguments[0].toBoolean();
-    result->setNull();
-}
-
-void TestRunner::setWillSendRequestReturnsNull(const CppArgumentList& arguments, CppVariant* result)
-{
-    if (arguments.size() > 0 && arguments[0].isBool())
-        m_willSendRequestShouldReturnNull = arguments[0].toBoolean();
-    result->setNull();
-}
-
 void TestRunner::setTabKeyCyclesThroughElements(const CppArgumentList& arguments, CppVariant* result)
 {
     if (arguments.size() > 0 && arguments[0].isBool())
diff --git a/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.h b/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.h
index faa8a70..84f1eb8 100644
--- a/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.h
+++ b/Tools/DumpRenderTree/chromium/TestRunner/src/TestRunner.h
@@ -114,8 +114,6 @@
     bool shouldStayOnPageAfterHandlingBeforeUnload() const;
     void setTitleTextDirection(WebKit::WebTextDirection);
     const std::set<std::string>* httpHeadersToClear() const;
-    bool shouldBlockRedirects() const;
-    bool willSendRequestShouldReturnNull() const;
     void setTopLoadingFrame(WebKit::WebFrame*, bool);
     WebKit::WebFrame* topLoadingFrame() const;
     void policyDelegateDone();
@@ -406,12 +404,6 @@
     // Causes WillSendRequest to clear certain headers.
     void setWillSendRequestClearHeader(const CppArgumentList&, CppVariant*);
 
-    // Causes WillSendRequest to block redirects.
-    void setWillSendRequestReturnsNullOnRedirect(const CppArgumentList&, CppVariant*);
-
-    // Causes WillSendRequest to return an empty request.
-    void setWillSendRequestReturnsNull(const CppArgumentList&, CppVariant*);
-
     // This function sets a flag that tells the test_shell to dump a descriptive
     // line for each resource load's priority and any time that priority
     // changes. It takes no arguments, and ignores any that may be present.
@@ -675,10 +667,6 @@
 
     bool m_shouldStayOnPageAfterHandlingBeforeUnload;
 
-    bool m_shouldBlockRedirects;
-
-    bool m_willSendRequestShouldReturnNull;
-
     bool m_shouldDumpResourcePriorities;
 
     std::set<std::string> m_httpHeadersToClear;
diff --git a/Tools/DumpRenderTree/chromium/TestRunner/src/WebTestProxy.cpp b/Tools/DumpRenderTree/chromium/TestRunner/src/WebTestProxy.cpp
index b902648..2e2659e 100644
--- a/Tools/DumpRenderTree/chromium/TestRunner/src/WebTestProxy.cpp
+++ b/Tools/DumpRenderTree/chromium/TestRunner/src/WebTestProxy.cpp
@@ -235,7 +235,7 @@
 
 void blockRequest(WebURLRequest& request)
 {
-    request.setURL(WebURL());
+    request.setURL(GURL("255.255.255.255"));
 }
 
 bool isLocalhost(const string& host)
@@ -1321,17 +1321,6 @@
         m_delegate->printMessage("\n");
     }
 
-    if (!redirectResponse.isNull() && m_testInterfaces->testRunner()->shouldBlockRedirects()) {
-        m_delegate->printMessage("Returning null for this redirect\n");
-        blockRequest(request);
-        return;
-    }
-
-    if (m_testInterfaces->testRunner()->willSendRequestShouldReturnNull()) {
-        blockRequest(request);
-        return;
-    }
-
     if (m_testInterfaces->testRunner()->httpHeadersToClear()) {
         const set<string> *clearHeaders = m_testInterfaces->testRunner()->httpHeadersToClear();
         for (set<string>::const_iterator header = clearHeaders->begin(); header != clearHeaders->end(); ++header)
diff --git a/Tools/Scripts/update-webgl-conformance-tests b/Tools/Scripts/update-webgl-conformance-tests
index b930836..cf9bd7f 100755
--- a/Tools/Scripts/update-webgl-conformance-tests
+++ b/Tools/Scripts/update-webgl-conformance-tests
@@ -29,8 +29,8 @@
 
 """Wrapper around webkitpy/layout_tests/update-webgl-conformance-tests.py"""
 
-import webkitpy.to_be_moved.update_webgl_conformance_tests
+import webkitpy.webgl.update_webgl_conformance_tests
 import sys
 
 if __name__ == '__main__':
-    sys.exit(webkitpy.to_be_moved.update_webgl_conformance_tests.main())
+    sys.exit(webkitpy.webgl.update_webgl_conformance_tests.main())
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/git.py b/Tools/Scripts/webkitpy/common/checkout/scm/git.py
index 71a34d6..c69a098 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/git.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/git.py
@@ -178,11 +178,11 @@
     def _branch_from_ref(self, ref):
         return ref.replace('refs/heads/', '')
 
-    def _current_branch(self):
+    def current_branch(self):
         return self._branch_from_ref(self._run_git(['symbolic-ref', '-q', 'HEAD']).strip())
 
     def _upstream_branch(self):
-        current_branch = self._current_branch()
+        current_branch = self.current_branch()
         return self._branch_from_ref(self.read_git_config('branch.%s.merge' % current_branch, cwd=self.checkout_root, executive=self._executive).strip())
 
     def merge_base(self, git_commit):
@@ -393,8 +393,14 @@
         self.commit_locally_with_message(message)
         return self.push_local_commits_to_server(username=username, password=password)
 
+    def checkout_branch(self, name):
+        self._run_git(['checkout', '-q', name])
+
+    def create_clean_branch(self, name):
+        self._run_git(['checkout', '-q', '-b', name, self.remote_branch_ref()])
+
     def _commit_on_branch(self, message, git_commit, username=None, password=None):
-        branch_name = self._current_branch()
+        branch_name = self.current_branch()
         commit_ids = self.commit_ids_from_commitish_arguments([git_commit])
 
         # We want to squash all this branch's commits into one commit with the proper description.
@@ -412,7 +418,7 @@
         # We wrap in a try...finally block so if anything goes wrong, we clean up the branches.
         commit_succeeded = True
         try:
-            self._run_git(['checkout', '-q', '-b', MERGE_BRANCH_NAME, self.remote_branch_ref()])
+            self.create_clean_branch(MERGE_BRANCH_NAME)
 
             for commit in commit_ids:
                 # We're on a different branch now, so convert "head" to the branch name.
@@ -430,7 +436,7 @@
         finally:
             # And then swap back to the original branch and clean up.
             self.discard_working_directory_changes()
-            self._run_git(['checkout', '-q', branch_name])
+            self.checkout_branch(branch_name)
             self.delete_branch(MERGE_BRANCH_NAME)
 
         return output
@@ -442,6 +448,9 @@
     def last_svn_commit_log(self):
         return self._run_git(['svn', 'log', '--limit=1'])
 
+    def blame(self, path):
+        return self._run_git(['blame', path])
+
     def svn_blame(self, path):
         return self._run_git(['svn', 'blame', path])
 
@@ -548,7 +557,7 @@
     def is_cleanly_tracking_remote_master(self):
         if self.has_working_directory_changes():
             return False
-        if self._current_branch() != self._branch_tracking_remote_master():
+        if self.current_branch() != self._branch_tracking_remote_master():
             return False
         if len(self.local_commits(self._branch_tracking_remote_master())) > 0:
             return False
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/scm.py b/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
index a972ca4..c57aec9 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
@@ -209,6 +209,9 @@
     def last_svn_commit_log(self):
         self._subclass_must_implement()
 
+    def blame(self, path):
+        self._subclass_must_implement()
+
     def svn_blame(self, path):
         self._subclass_must_implement()
 
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py b/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
index 961a7fd..192c03c 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
@@ -32,6 +32,8 @@
 
 
 class MockSCM(object):
+    executable_name = "MockSCM"
+
     def __init__(self, filesystem=None, executive=None):
         self.checkout_root = "/mock-checkout"
         self.added_paths = set()
@@ -52,6 +54,21 @@
     def discard_working_directory_changes(self):
         pass
 
+    def ensure_cleanly_tracking_remote_master(self):
+        pass
+
+    def current_branch(self):
+        return "mock-branch-name"
+
+    def checkout_branch(self, name):
+        pass
+
+    def create_clean_branch(self, name):
+        pass
+
+    def delete_branch(self, name):
+        pass
+
     def supports_local_commits(self):
         return True
 
@@ -126,6 +143,15 @@
     def svn_revision_from_commit_text(self, commit_text):
         return "49824"
 
+    def svn_revision_from_git_commit(self, git_commit):
+        if git_commit == '6469e754a1':
+            return 1234
+        if git_commit == '624c3081c0':
+            return 5678
+        if git_commit == '624caaaaaa':
+            return 10000
+        return None
+
     def delete(self, path):
         return self.delete_list([path])
 
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py b/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
index dc9ad53..7c37ac8 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
@@ -361,6 +361,9 @@
         # http://svnbook.red-bean.com/en/1.0/ch03s03.html
         return self.svn_commit_log('BASE')
 
+    def blame(self, path):
+        return self._run_svn(['blame', path])
+
     def svn_blame(self, path):
         return self._run_svn(['blame', path])
 
diff --git a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
index 3ca01a3..25607df 100644
--- a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
+++ b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
@@ -79,7 +79,7 @@
 
     def fetch_layout_test_results(self, results_url):
         # FIXME: This should cache that the result was a 404 and stop hitting the network.
-        results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results(results_url, "full_results.json"))
+        results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results(results_url, "failing_results.json"))
         return LayoutTestResults.results_from_string(results_file)
 
     def url_encoded_name(self):
diff --git a/Tools/Scripts/webkitpy/common/system/executive.py b/Tools/Scripts/webkitpy/common/system/executive.py
index 1af0831..94121da 100644
--- a/Tools/Scripts/webkitpy/common/system/executive.py
+++ b/Tools/Scripts/webkitpy/common/system/executive.py
@@ -484,6 +484,9 @@
             string_args = self._stringify_args(args)
         return subprocess.Popen(string_args, **kwargs)
 
+    def call(self, args, **kwargs):
+        return subprocess.call(self._stringify_args(args), **kwargs)
+
     def run_in_parallel(self, command_lines_and_cwds, processes=None):
         """Runs a list of (cmd_line list, cwd string) tuples in parallel and returns a list of (retcode, stdout, stderr) tuples."""
         assert len(command_lines_and_cwds)
diff --git a/Tools/Scripts/webkitpy/common/system/executive_mock.py b/Tools/Scripts/webkitpy/common/system/executive_mock.py
index 48a9bc1..6a3d018 100644
--- a/Tools/Scripts/webkitpy/common/system/executive_mock.py
+++ b/Tools/Scripts/webkitpy/common/system/executive_mock.py
@@ -143,6 +143,9 @@
             self._proc = MockProcess()
         return self._proc
 
+    def call(self, args, **kwargs):
+        _log.info('Mock call: %s' % args)
+
     def run_in_parallel(self, commands):
         num_previous_calls = len(self.calls)
         command_outputs = []
diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
index 0bd8f5e..72c0e26 100644
--- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
@@ -984,6 +984,9 @@
     def model(self):
         return self._model
 
+    def get_needs_rebaseline_failures(self):
+        return self._model.get_test_set_for_keyword(TestExpectationParser.NEEDS_REBASELINE_MODIFIER)
+
     def get_rebaselining_failures(self):
         return self._model.get_test_set(REBASELINE)
 
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
index e11307c..14db692 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
@@ -145,6 +145,10 @@
         port._executive = MockExecutive2(run_command_fn=port._mock_adb.run_command)
         return port
 
+    def make_wdiff_available(self, port):
+        port._wdiff_available = True
+        port._host_port._wdiff_available = True
+
     # Test that content_shell currently is the only supported driver.
     def test_non_content_shell_driver(self):
         self.assertRaises(self.make_port, options=optparse.Values({'driver_name': 'foobar'}))
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/base.py b/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 5b9c9de..55f42ec 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -1139,6 +1139,8 @@
             error_handler=self._handle_wdiff_error)
         return self._format_wdiff_output_as_html(wdiff)
 
+    _wdiff_error_html = "Failed to run wdiff, see error log."
+
     def wdiff_text(self, actual_filename, expected_filename):
         """Returns a string of HTML indicating the word-level diff of the
         contents of the two filenames. Returns an empty string if word-level
@@ -1148,12 +1150,16 @@
         try:
             # It's possible to raise a ScriptError we pass wdiff invalid paths.
             return self._run_wdiff(actual_filename, expected_filename)
-        except OSError, e:
+        except OSError as e:
             if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]:
                 # Silently ignore cases where wdiff is missing.
                 self._wdiff_available = False
                 return ""
             raise
+        except ScriptError as e:
+            _log.error("Failed to run wdiff: %s" % e)
+            self._wdiff_available = False
+            return self._wdiff_error_html
 
     # This is a class variable so we can test error output easily.
     _pretty_patch_error_html = "Failed to run PrettyPatch, see error log."
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
index 8ab92cf..2ee659c 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
@@ -125,6 +125,9 @@
     def show_results_html_file(self, results_filename):
         pass
 
+    def _make_wdiff_available(self):
+        self.__delegate._wdiff_available = True
+
 
 def main(argv, host, stdin, stdout, stderr):
     """Run the tests."""
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
index 56e1581..1dde3b9 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
@@ -45,11 +45,14 @@
 
 class MockDRTPortTest(port_testcase.PortTestCase):
 
-    def make_port(self, options=mock_options):
-        host = MockSystemHost()
+    def make_port(self, host=None, options=mock_options):
+        host = host or MockSystemHost()
         test.add_unit_tests_to_mock_filesystem(host.filesystem)
         return mock_drt.MockDRTPort(host, port_name='mock-mac', options=options)
 
+    def make_wdiff_available(self, port):
+        port._make_wdiff_available()
+
     def test_port_name_in_constructor(self):
         self.assertTrue(mock_drt.MockDRTPort(MockSystemHost(), port_name='mock-test'))
 
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index 82524bc..6b5baa3 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -88,6 +88,9 @@
         port._config.build_directory = lambda configuration: '/mock-build'
         return port
 
+    def make_wdiff_available(self, port):
+        port._wdiff_available = True
+
     def test_default_max_locked_shards(self):
         port = self.make_port()
         port.default_child_processes = lambda: 16
@@ -174,6 +177,20 @@
         port = self.make_port()
         port.check_wdiff()
 
+    def test_wdiff_text_fails(self):
+        host = MockSystemHost(os_name=self.os_name, os_version=self.os_version)
+        host.executive = MockExecutive(should_throw=True)
+        port = self.make_port(host=host)
+        port._executive = host.executive  # AndroidPortTest.make_port sets its own executive, so reset that as well.
+
+        # This should raise a ScriptError that gets caught and turned into the
+        # error text, and also mark wdiff as not available.
+        self.make_wdiff_available(port)
+        self.assertTrue(port.wdiff_available())
+        diff_txt = port.wdiff_text("/tmp/foo.html", "/tmp/bar.html")
+        self.assertEqual(diff_txt, port._wdiff_error_html)
+        self.assertFalse(port.wdiff_available())
+
     def test_test_configuration(self):
         port = self.make_port()
         self.assertTrue(port.test_configuration())
diff --git a/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
index 48f1ebb..717172d 100644
--- a/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
+++ b/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
@@ -29,7 +29,10 @@
 import json
 import logging
 import optparse
+import re
 import sys
+import time
+import urllib2
 
 from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
 from webkitpy.common.memoized import memoized
@@ -299,7 +302,7 @@
             verbose_args = ['--verbose'] if verbose else []
             stderr = self._tool.executive.run_command([self._tool.path()] + verbose_args + args, cwd=self._tool.scm().checkout_root, return_stderr=True)
             for line in stderr.splitlines():
-                print >> sys.stderr, line
+                _log.warning(line)
         except ScriptError, e:
             _log.error(e)
 
@@ -524,3 +527,186 @@
             _log.debug("rebaseline-json: " + str(test_prefix_list))
 
         self._rebaseline(options, test_prefix_list)
+
+
+class AutoRebaseline(AbstractParallelRebaselineCommand):
+    name = "auto-rebaseline"
+    help_text = "Rebaselines any NeedsRebaseline lines in TestExpectations that have cycled through all the bots."
+    AUTO_REBASELINE_BRANCH_NAME = "auto-rebaseline-temporary-branch"
+
+    # Rietveld uploader stinks. Limit the number of rebaselines in a given patch to keep upload from failing.
+    # FIXME: http://crbug.com/263676 Obviously we should fix the uploader here.
+    MAX_LINES_TO_REBASELINE = 200
+
+    def __init__(self):
+        super(AutoRebaseline, self).__init__(options=[
+            # FIXME: Remove this option.
+            self.no_optimize_option,
+            # FIXME: Remove this option.
+            self.results_directory_option,
+            ])
+
+    def latest_revision_processed_on_all_bots(self):
+        revisions = []
+        for builder_name in self._release_builders():
+            builder = self._tool.buildbot_for_builder_name(builder_name).builder_with_name(builder_name)
+            result = builder.latest_layout_test_results()
+            if result.run_was_interrupted():
+                _log.error("Can't rebaseline. The latest run on %s did not complete." % builder_name)
+                return 0
+            revisions.append(result.blink_revision())
+        return int(min(revisions))
+
+    def tests_to_rebaseline(self, tool, min_revision, print_revisions):
+        port = tool.port_factory.get()
+        expectations_file_path = port.path_to_generic_test_expectations_file()
+
+        tests = set()
+        revision = None
+        author = None
+        bugs = set()
+
+        for line in tool.scm().blame(expectations_file_path).split("\n"):
+            if "NeedsRebaseline" not in line:
+                continue
+            parsed_line = re.match("^(\S*)[^(]*\((\S*).*?([^ ]*)\ \[[^[]*$", line)
+
+            commit_hash = parsed_line.group(1)
+            svn_revision = tool.scm().svn_revision_from_git_commit(commit_hash)
+
+            test = parsed_line.group(3)
+            if print_revisions:
+                _log.info("%s is waiting for r%s" % (test, svn_revision))
+
+            if not svn_revision or svn_revision > min_revision:
+                continue
+
+            if revision and svn_revision != revision:
+                continue
+
+            if not revision:
+                revision = svn_revision
+                author = parsed_line.group(2)
+
+            bugs.update(re.findall("crbug\.com\/(\d+)", line))
+            tests.add(test)
+
+            if len(tests) >= self.MAX_LINES_TO_REBASELINE:
+                _log.info("Too many tests to rebaseline in one patch. Doing the first %d." % self.MAX_LINES_TO_REBASELINE)
+                break
+
+        return tests, revision, author, bugs
+
+    def link_to_patch(self, revision):
+        return "http://src.chromium.org/viewvc/blink?view=revision&revision=" + str(revision)
+
+    def commit_message(self, author, revision, bugs):
+        bug_string = ""
+        if bugs:
+            bug_string = "BUG=%s\n" % ",".join(bugs)
+
+        return """Auto-rebaseline for r%s
+
+%s
+
+%sTBR=%s
+""" % (revision, self.link_to_patch(revision), bug_string, author)
+
+    def get_test_prefix_list(self, tests):
+        test_prefix_list = {}
+
+        for builder_name in self._release_builders():
+            port_name = builders.port_name_for_builder_name(builder_name)
+            port = self._tool.port_factory.get(port_name)
+            expectations = TestExpectations(port, include_overrides=True)
+            for test in expectations.get_needs_rebaseline_failures():
+                if test not in tests:
+                    continue
+                if test not in test_prefix_list:
+                    test_prefix_list[test] = {}
+                test_prefix_list[test][builder_name] = BASELINE_SUFFIX_LIST
+
+        return test_prefix_list
+
+    def _run_git_cl_command(self, options, command):
+        subprocess_command = ['git', 'cl'] + command
+        if options.verbose:
+            subprocess_command.append('--verbose')
+        # Use call instead of run_command so that stdout doesn't get swallowed.
+        self._tool.executive.call(subprocess_command)
+
+    # FIXME: Move this somewhere more general.
+    def tree_status(self):
+        blink_tree_status_url = "http://blink-status.appspot.com/status"
+        status = urllib2.urlopen(blink_tree_status_url).read().lower()
+        if status.find('closed') != -1 or status == 0:
+            return 'closed'
+        elif status.find('open') != -1 or status == 1:
+            return 'open'
+        return 'unknown'
+
+    def execute(self, options, args, tool):
+        if tool.scm().executable_name == "svn":
+            _log.error("Auto rebaseline only works with a git checkout.")
+            return
+
+        if tool.scm().has_working_directory_changes():
+            _log.error("Cannot proceed with working directory changes. Clean working directory first.")
+            return
+
+        min_revision = self.latest_revision_processed_on_all_bots()
+        if not min_revision:
+            return
+
+        if options.verbose:
+            _log.info("Bot min revision is %s." % min_revision)
+
+        tests, revision, author, bugs = self.tests_to_rebaseline(tool, min_revision, print_revisions=options.verbose)
+        test_prefix_list = self.get_test_prefix_list(tests)
+
+        if not tests:
+            _log.debug('No tests to rebaseline.')
+            return
+        _log.info('Rebaselining %s for r%s by %s.' % (list(tests), revision, author))
+
+        if self.tree_status() == 'closed':
+            _log.info('Cannot proceed. Tree is closed.')
+            return
+
+        try:
+            old_branch_name = tool.scm().current_branch()
+            tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
+            tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME)
+
+            self._rebaseline(options, test_prefix_list)
+
+            tool.scm().commit_locally_with_message(self.commit_message(author, revision, bugs))
+
+            # FIXME: It would be nice if we could dcommit the patch without uploading, but still
+            # go through all the precommit hooks. For rebaselines with lots of files, uploading
+            # takes a long time and sometimes fails, but we don't want to commit if, e.g. the
+            # tree is closed.
+            self._run_git_cl_command(options, ['upload', '-f'])
+            self._run_git_cl_command(options, ['dcommit', '-f'])
+        finally:
+            self._run_git_cl_command(options, ['set_close'])
+            tool.scm().ensure_cleanly_tracking_remote_master()
+            tool.scm().checkout_branch(old_branch_name)
+            tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
+
+
+class RebaselineOMatic(AbstractDeclarativeCommand):
+    name = "rebaseline-o-matic"
+    help_text = "Calls webkit-patch auto-rebaseline in a loop."
+
+    SLEEP_TIME_IN_SECONDS = 30
+
+    def execute(self, options, args, tool):
+        while True:
+            tool.executive.run_command(['git', 'pull'])
+            rebaseline_command = [tool.filesystem.join(tool.scm().checkout_root, 'Tools', 'Scripts', 'webkit-patch'), 'auto-rebaseline']
+            if options.verbose:
+                rebaseline_command.append('--verbose')
+            # Use call instead of run_command so that stdout doesn't get swallowed.
+            tool.executive.call(rebaseline_command)
+            time.sleep(self.SLEEP_TIME_IN_SECONDS)
diff --git a/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
index 98ee4ed..be814a5 100644
--- a/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -504,3 +504,158 @@
             ['passes/text-expected.png: (no baselines found)',
              'passes/text-expected.txt:',
              '  (generic): 123456'])
+
+
+class TestAutoRebaseline(_BaseTestCase):
+    command_constructor = AutoRebaseline
+
+    def _write_test_file(self, port, path, contents):
+        abs_path = self.tool.filesystem.join(port.layout_tests_dir(), path)
+        self.tool.filesystem.write_text_file(abs_path, contents)
+
+    def setUp(self):
+        super(TestAutoRebaseline, self).setUp()
+        self.command.latest_revision_processed_on_all_bots = lambda: 9000
+
+    def test_tests_to_rebaseline(self):
+        def blame(path):
+            return """
+624c3081c0 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000   11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ]
+624c3081c0 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   13) Bug(foo) path/to/rebaseline-without-bug-number.html [ NeedsRebaseline ]
+624c3081c0 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000   11) crbug.com/24182 [ Debug ] path/to/rebaseline-with-modifiers.html [ NeedsRebaseline ]
+624c3081c0 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   12) crbug.com/24182 crbug.com/234 path/to/rebaseline-without-modifiers.html [ NeedsRebaseline ]
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/rebaseline-new-revision.html [ NeedsRebaseline ]
+624caaaaaa path/to/TestExpectations                   (foo@chromium.org        2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ]
+0000000000 path/to/TestExpectations                   (foo@chromium.org        2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ]
+"""
+        self.tool.scm().blame = blame
+
+        min_revision = 9000
+        self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), (
+                set(['path/to/rebaseline-without-bug-number.html', 'path/to/rebaseline-with-modifiers.html', 'path/to/rebaseline-without-modifiers.html']),
+                5678,
+                'foobarbaz1@chromium.org',
+                set(['24182', '234'])))
+
+    def test_tests_to_rebaseline_over_limit(self):
+        def blame(path):
+            result = ""
+            for i in range(0, self.command.MAX_LINES_TO_REBASELINE + 1):
+                result += "624c3081c0 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   13) crbug.com/24182 path/to/rebaseline-%s.html [ NeedsRebaseline ]\n" % i
+            return result
+        self.tool.scm().blame = blame
+
+        expected_list_of_tests = []
+        for i in range(0, self.command.MAX_LINES_TO_REBASELINE):
+            expected_list_of_tests.append("path/to/rebaseline-%s.html" % i)
+
+        min_revision = 9000
+        self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), (
+                set(expected_list_of_tests),
+                5678,
+                'foobarbaz1@chromium.org',
+                set(['24182'])))
+
+    def test_commit_message(self):
+        author = "foo@chromium.org"
+        revision = 1234
+        bugs = set()
+        self.assertEqual(self.command.commit_message(author, revision, bugs),
+            """Auto-rebaseline for r1234
+
+http://src.chromium.org/viewvc/blink?view=revision&revision=1234
+
+TBR=foo@chromium.org
+""")
+
+        bugs = set(["234", "345"])
+        self.assertEqual(self.command.commit_message(author, revision, bugs),
+            """Auto-rebaseline for r1234
+
+http://src.chromium.org/viewvc/blink?view=revision&revision=1234
+
+BUG=234,345
+TBR=foo@chromium.org
+""")
+
+    def test_no_needs_rebaseline_lines(self):
+        def blame(path):
+            return """
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000   11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ]
+"""
+        self.tool.scm().blame = blame
+
+        self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool)
+        self.assertEqual(self.tool.executive.calls, [])
+
+    def test_execute(self):
+        def blame(path):
+            return """
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000   11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ]
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   13) Bug(foo) path/to/rebaseline-without-bug-number.html [ NeedsRebaseline ]
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000   11) crbug.com/24182 [ SnowLeopard ] path/to/rebaseline-with-modifiers.html [ NeedsRebaseline ]
+6469e754a1 path/to/TestExpectations                   (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/rebaseline-without-modifiers.html [ NeedsRebaseline ]
+624caaaaaa path/to/TestExpectations                   (foo@chromium.org        2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ]
+0000000000 path/to/TestExpectations                   (foo@chromium.org        2013-04-28 04:52:41 +0000   12) crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ]
+"""
+        self.tool.scm().blame = blame
+
+        test_port = self.tool.port_factory.get('test')
+        original_get = self.tool.port_factory.get
+
+        def get_test_port(port_name=None, options=None, **kwargs):
+            if not port_name:
+                return test_port
+            return original_get(port_name, options, **kwargs)
+        # Need to make sure all the ports grabbed use the test checkout path instead of the mock checkout path.
+        self.tool.port_factory.get = get_test_port
+
+        self.tool.filesystem.write_text_file(test_port.path_to_generic_test_expectations_file(), """
+crbug.com/24182 [ Debug ] path/to/norebaseline.html [ Rebaseline ]
+Bug(foo) path/to/rebaseline-without-bug-number.html [ NeedsRebaseline ]
+crbug.com/24182 [ SnowLeopard ] path/to/rebaseline-with-modifiers.html [ NeedsRebaseline ]
+crbug.com/24182 path/to/rebaseline-without-modifiers.html [ NeedsRebaseline ]
+crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ]
+crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ]
+""")
+
+        self._write_test_file(test_port, 'path/to/rebaseline-without-bug-number.html', "Dummy test contents")
+        self._write_test_file(test_port, 'path/to/rebaseline-with-modifiers.html', "Dummy test contents")
+        self._write_test_file(test_port, 'path/to/rebaseline-without-modifiers.html', "Dummy test contents")
+
+        old_exact_matches = builders._exact_matches
+        try:
+            builders._exact_matches = {
+                "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
+                "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])},
+            }
+
+            self.command.tree_status = lambda: 'closed'
+            self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool)
+            self.assertEqual(self.tool.executive.calls, [])
+
+            self.command.tree_status = lambda: 'open'
+
+            self.tool.executive.calls = []
+            self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool)
+            self.assertEqual(self.tool.executive.calls, [
+                [
+                    ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK Leopard', '--test', 'path/to/rebaseline-without-modifiers.html'],
+                    ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-without-modifiers.html'],
+                    ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK Leopard', '--test', 'path/to/rebaseline-without-bug-number.html'],
+                    ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-without-bug-number.html'],
+                    ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-with-modifiers.html'],
+                ],
+                [
+                    ['echo', 'rebaseline-test-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK Leopard', '--test', 'path/to/rebaseline-without-modifiers.html'],
+                    ['echo', 'rebaseline-test-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-without-modifiers.html'],
+                    ['echo', 'rebaseline-test-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK Leopard', '--test', 'path/to/rebaseline-without-bug-number.html'],
+                    ['echo', 'rebaseline-test-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-without-bug-number.html'],
+                    ['echo', 'rebaseline-test-internal', '--suffixes', 'png,wav,txt', '--builder', 'MOCK SnowLeopard', '--test', 'path/to/rebaseline-with-modifiers.html'],
+                ],
+                ['echo', 'optimize-baselines', '--suffixes', 'wav,txt,png', 'path/to/rebaseline-without-modifiers.html'],
+                ['echo', 'optimize-baselines', '--suffixes', 'wav,txt,png', 'path/to/rebaseline-without-bug-number.html'],
+                ['echo', 'optimize-baselines', '--suffixes', 'wav,txt,png', 'path/to/rebaseline-with-modifiers.html'],
+            ])
+        finally:
+            builders._exact_matches = old_exact_matches
diff --git a/Tools/Scripts/webkitpy/to_be_moved/__init__.py b/Tools/Scripts/webkitpy/webgl/__init__.py
similarity index 100%
rename from Tools/Scripts/webkitpy/to_be_moved/__init__.py
rename to Tools/Scripts/webkitpy/webgl/__init__.py
diff --git a/Tools/Scripts/webkitpy/to_be_moved/update_webgl_conformance_tests.py b/Tools/Scripts/webkitpy/webgl/update_webgl_conformance_tests.py
similarity index 100%
rename from Tools/Scripts/webkitpy/to_be_moved/update_webgl_conformance_tests.py
rename to Tools/Scripts/webkitpy/webgl/update_webgl_conformance_tests.py
diff --git a/Tools/Scripts/webkitpy/to_be_moved/update_webgl_conformance_tests_unittest.py b/Tools/Scripts/webkitpy/webgl/update_webgl_conformance_tests_unittest.py
similarity index 97%
rename from Tools/Scripts/webkitpy/to_be_moved/update_webgl_conformance_tests_unittest.py
rename to Tools/Scripts/webkitpy/webgl/update_webgl_conformance_tests_unittest.py
index 9a7823e..42e0925 100644
--- a/Tools/Scripts/webkitpy/to_be_moved/update_webgl_conformance_tests_unittest.py
+++ b/Tools/Scripts/webkitpy/webgl/update_webgl_conformance_tests_unittest.py
@@ -29,7 +29,7 @@
 """Unit tests for update_webgl_conformance_tests."""
 
 import webkitpy.thirdparty.unittest2 as unittest
-from webkitpy.to_be_moved import update_webgl_conformance_tests as webgl
+from webkitpy.webgl import update_webgl_conformance_tests as webgl
 
 
 def construct_script(name):
diff --git a/Tools/TestResultServer/model/datastorefile.py b/Tools/TestResultServer/model/datastorefile.py
index 8dca34d..6161c37 100755
--- a/Tools/TestResultServer/model/datastorefile.py
+++ b/Tools/TestResultServer/model/datastorefile.py
@@ -32,7 +32,7 @@
 from google.appengine.ext import blobstore
 from google.appengine.ext import db
 
-MAX_DATA_ENTRY_PER_FILE = 20
+MAX_DATA_ENTRY_PER_FILE = 30
 MAX_ENTRY_LEN = 1000 * 1000
 
 
diff --git a/Tools/TestResultServer/model/jsonresults.py b/Tools/TestResultServer/model/jsonresults.py
index 35b56c9..9976b67 100755
--- a/Tools/TestResultServer/model/jsonresults.py
+++ b/Tools/TestResultServer/model/jsonresults.py
@@ -449,7 +449,7 @@
         aggregated_json[builder][FAILURE_MAP_KEY] = CHAR_TO_FAILURE
 
         is_debug_builder = re.search(r"(Debug|Dbg)", builder, re.I)
-        run_time_pruning_threshold = 2 * JSON_RESULTS_MIN_TIME if is_debug_builder else JSON_RESULTS_MIN_TIME
+        run_time_pruning_threshold = 3 * JSON_RESULTS_MIN_TIME if is_debug_builder else JSON_RESULTS_MIN_TIME
         cls._normalize_results(aggregated_json[builder][TESTS_KEY], num_runs, run_time_pruning_threshold)
         return cls._generate_file_data(aggregated_json, sort_keys), 200
 
diff --git a/Tools/TestResultServer/model/jsonresults_unittest.py b/Tools/TestResultServer/model/jsonresults_unittest.py
index def7004..cef590b 100755
--- a/Tools/TestResultServer/model/jsonresults_unittest.py
+++ b/Tools/TestResultServer/model/jsonresults_unittest.py
@@ -848,7 +848,7 @@
             {"builds": ["2", "1"],
              "tests": {"001.html": {
                            "results": [[200, PASS]],
-                           "times": [[200, 2 * jsonresults.JSON_RESULTS_MIN_TIME]]},
+                           "times": [[200, 3 * jsonresults.JSON_RESULTS_MIN_TIME]]},
                        "002.html": {
                            "results": [[10, TEXT]],
                            "times": [[10, 0]]}}},
@@ -867,7 +867,7 @@
             {"builds": ["3", "2", "1"],
              "tests": {"001.html": {
                            "results": [[201, PASS]],
-                           "times": [[1, 1], [200, 2 * jsonresults.JSON_RESULTS_MIN_TIME]]},
+                           "times": [[1, 1], [200, 3 * jsonresults.JSON_RESULTS_MIN_TIME]]},
                        "002.html": {
                            "results": [[1, PASS], [10, TEXT]],
                            "times": [[11, 0]]}}})
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
index c944b4c..b426032 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
@@ -31,7 +31,6 @@
 //////////////////////////////////////////////////////////////////////////////
 var FORWARD = 'forward';
 var BACKWARD = 'backward';
-var GTEST_MODIFIERS = ['FLAKY', 'FAILS', 'MAYBE', 'DISABLED'];
 var TEST_URL_BASE_PATH_FOR_BROWSING = 'http://src.chromium.org/viewvc/blink/trunk/LayoutTests/';
 var TEST_URL_BASE_PATH_FOR_XHR = 'http://src.chromium.org/blink/trunk/LayoutTests/';
 var TEST_RESULTS_BASE_PATH = 'http://build.chromium.org/f/chromium/layout_test_results/';
@@ -110,7 +109,8 @@
             function() {
                 // Get all possible headers since the actual used set of headers
                 // depends on the values in historyInstance.dashboardSpecificState, which are currently being set.
-                var headers = tableHeaders(true);
+                var getAllTableHeaders = true;
+                var headers = tableHeaders(getAllTableHeaders);
                 for (var i = 0; i < headers.length; i++) {
                     if (value == sortColumnFromTableHeader(headers[i]))
                         return true;
@@ -315,29 +315,16 @@
     return individualTestsForSubstringList();
 }
 
-function substringList()
+function splitTestList()
 {
-    // Convert windows slashes to unix slashes.
-    var tests = g_history.dashboardSpecificState.tests.replace(/\\/g, '/');
-    var separator = string.contains(tests, ' ') ? ' ' : ',';
-    var testList = tests.split(separator);
-
-    if (g_history.isLayoutTestResults())
-        return testList;
-
-    var testListWithoutModifiers = [];
-    testList.forEach(function(path) {
-        GTEST_MODIFIERS.forEach(function(modifier) {
-            path = path.replace('.' + modifier + '_', '.');
-        });
-        testListWithoutModifiers.push(path);
-    });
-    return testListWithoutModifiers;
+    // Convert windows slashes to unix slashes and spaces/newlines to commas.
+    var tests = g_history.dashboardSpecificState.tests.replace(/\\/g, '/').replace('\n', ' ').replace(/\s+/g, ',');
+    return tests.split(',');
 }
 
 function individualTestsForSubstringList()
 {
-    var testList = substringList();
+    var testList = splitTestList();
     // If listing a lot of tests, assume you've passed in an explicit list of tests
     // instead of patterns to match against. The matching code below is super slow.
     //
@@ -662,19 +649,15 @@
     return html;
 }
 
-function htmlForSingleTestRow(test)
+function htmlForSingleTestRow(test, showBuilderNames)
 {
     var headers = tableHeaders();
     var html = '';
     for (var i = 0; i < headers.length; i++) {
         var header = headers[i];
         if (string.startsWith(header, 'test') || string.startsWith(header, 'builder')) {
-            // If isCrossBuilderView() is true, we're just viewing a single test
-            // with results for many builders, so the first column is builder names
-            // instead of test paths.
-            var testCellClassName = 'test-link' + (isCrossBuilderView() ? ' builder-name' : '');
-            var testCellHTML = isCrossBuilderView() ? test.builder : '<span class="link" onclick="g_history.setQueryParameter(\'tests\',\'' + test.test +'\');">' + test.test + '</span>';
-
+            var testCellClassName = 'test-link' + (showBuilderNames ? ' builder-name' : '');
+            var testCellHTML = showBuilderNames ? test.builder : '<span class="link" onclick="g_history.setQueryParameter(\'tests\',\'' + test.test +'\');">' + test.test + '</span>';
             html += '<tr><td class="' + testCellClassName + '">' + testCellHTML;
         } else if (string.startsWith(header, 'bugs'))
             // FIXME: linkify bugs.
@@ -812,7 +795,8 @@
     var shownBuilders = [];
     for (var j = 0; j < testResults.length; j++) {
         shownBuilders.push(testResults[j].builder);
-        html += htmlForSingleTestRow(testResults[j]);
+        var showBuilderNames = true;
+        html += htmlForSingleTestRow(testResults[j], showBuilderNames);
     }
 
     var skippedBuilders = []
@@ -1061,7 +1045,7 @@
     expectationsContainer.appendChild(container);
     for (var i = 0; i < failureIndexes.length; i++) {
         // FIXME: This doesn't seem to work anymore. Did the paths change?
-        // Once that's resolved, see if we need to try each GTEST_MODIFIERS prefix as well.
+        // Once that's resolved, see if we need to try each gtest modifier prefix as well.
         var buildNumber = g_resultsByBuilder[builder].buildNumbers[failureIndexes[i]];
         var pathToLog = builders.master(builder).logPath(builder, buildNumber) + pathToFailureLog(test);
         appendNonWebKitResults(container, pathToLog, 'non-webkit-results');
@@ -1276,8 +1260,9 @@
     var testsHTML = '';
     if (filteredResults.length) {
         var tableRowsHTML = '';
+        var showBuilderNames = false;
         for (var i = 0; i < filteredResults.length; i++)
-            tableRowsHTML += htmlForSingleTestRow(filteredResults[i])
+            tableRowsHTML += htmlForSingleTestRow(filteredResults[i], showBuilderNames)
         testsHTML = htmlForTestTable(tableRowsHTML);
     } else {
         if (g_history.isLayoutTestResults())
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
index a9c39c9..bcccd2f 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
@@ -62,17 +62,12 @@
 var FAILURE_MAP = {"A": "AUDIO", "C": "CRASH", "F": "TEXT", "I": "IMAGE", "O": "MISSING",
     "N": "NO DATA", "P": "PASS", "T": "TIMEOUT", "Y": "NOTRUN", "X": "SKIP", "Z": "IMAGE+TEXT"}
 
-test('substringList', 2, function() {
+test('splitTestList', 1, function() {
     var historyInstance = new history.History(flakinessConfig);
     // FIXME(jparent): Remove this once global isn't used.
     g_history = historyInstance;
-    historyInstance.crossDashboardState.testType = 'gtest';
-    historyInstance.dashboardSpecificState.tests = 'test.FLAKY_foo test.FAILS_foo1 test.DISABLED_foo2 test.MAYBE_foo3 test.foo4';
-    equal(substringList().toString(), 'test.foo,test.foo1,test.foo2,test.foo3,test.foo4');
-
-    historyInstance.crossDashboardState.testType = 'layout-tests';
-    historyInstance.dashboardSpecificState.tests = 'foo/bar.FLAKY_foo.html';
-    equal(substringList().toString(), 'foo/bar.FLAKY_foo.html');
+    historyInstance.dashboardSpecificState.tests = 'test.foo test.foo1\ntest.foo2\ntest.foo3,foo\\bar\\baz.html';
+    equal(splitTestList().toString(), 'test.foo,test.foo1,test.foo2,test.foo3,foo/bar/baz.html');
 });
 
 test('headerForTestTableHtml', 1, function() {
@@ -147,7 +142,7 @@
                 '<th sortValue=flakiness colspan=10000><div class=table-header-content><span></span><span class=header-text>flakiness (numbers are runtimes in seconds)</span></div></th>' +
             '</tr></thead>' +
             '<tbody><tr>' +
-                '<td class="test-link"><span class="link" onclick="g_history.setQueryParameter(\'tests\',\'dummytest.html\');">dummytest.html</span>' +
+                '<td class="test-link builder-name">WebKit Linux' +
                 '<td class=options-container>' +
                     '<div><a href="http://crbug.com/1234">crbug.com/1234</a></div>' +
                     '<div><a href="http://webkit.org/5678">webkit.org/5678</a></div>' +