Merge from Chromium at DEPS revision r190564

This commit was generated by merge_to_master.py.

Change-Id: Ie7b06f551ab581fd65e508d8ab5e40eda59c14b2
diff --git a/Tools/TestResultServer/static-dashboards/aggregate_results.html b/Tools/TestResultServer/static-dashboards/aggregate_results.html
index fa033a5..2790ddc 100644
--- a/Tools/TestResultServer/static-dashboards/aggregate_results.html
+++ b/Tools/TestResultServer/static-dashboards/aggregate_results.html
@@ -52,248 +52,11 @@
 </style>
 <script src="builders.js"></script>
 <script src="loader.js"></script>
+<script src="string.js"></script>
+<script src="history.js"></script>
 <script src="dashboard_base.js"></script>
-<script>
-// @fileoverview Creates a dashboard for tracking number of passes/failures per run.
-//
-// Currently, only webkit tests are supported, but adding other test types
-// should just require the following steps:
-//     -generate results.json for these tests
-//     -copy them to the appropriate location
-//     -add the builder name to the list of builders in dashboard_base.js.
-
-//////////////////////////////////////////////////////////////////////////////
-// Methods and objects from dashboard_base.js to override.
-//////////////////////////////////////////////////////////////////////////////
-function generatePage()
-{
-    var html = htmlForTestTypeSwitcher(true) + '<br>';
-    for (var builder in g_builders)
-        html += htmlForBuilder(builder);
-    document.body.innerHTML = html;
-}
-
-function handleValidHashParameter(key, value)
-{
-    switch(key) {
-    case 'rawValues':
-        g_currentState[key] = value == 'true';
-        return true;
-
-    default:
-        return false;
-    }
-}
-
-g_defaultDashboardSpecificStateValues = {
-    rawValues: false
-};
-
-function htmlForBuilder(builder)
-{
-    var results = g_resultsByBuilder[builder];
-    // Some keys were added later than others, so they don't have as many
-    // builds. Use the shortest.
-    // FIXME: Once 500 runs have finished, we can get rid of passing this
-    // around and just assume all keys have the same number of builders for a
-    // given builder.
-    var numColumns = results[ALL_FIXABLE_COUNT_KEY].length;
-    var html = '<div class=container><h2>' + builder + '</h2>';
-
-    if (g_currentState.rawValues)
-        html += rawValuesHTML(results, numColumns);
-    else {
-        html += '<a href="timeline_explorer.html' + (location.hash ? location.hash + '&' : '#') + 'builder=' + builder + '">' +
-            chartHTML(results, numColumns) + '</a>';
-    }
-
-    html += '</div>';
-    return html;
-}
-
-function rawValuesHTML(results, numColumns)
-{
-    var html = htmlForSummaryTable(results, numColumns) +
-        htmlForTestType(results, FIXABLE_COUNTS_KEY, FIXABLE_DESCRIPTION, numColumns);
-    if (isLayoutTestResults()) {
-        html += htmlForTestType(results, DEFERRED_COUNTS_KEY, DEFERRED_DESCRIPTION, numColumns) +
-            htmlForTestType(results, WONTFIX_COUNTS_KEY, WONTFIX_DESCRIPTION, numColumns);
-    }
-    return html;
-}
-
-function chartHTML(results, numColumns)
-{
-    var shouldShowWebKitRevisions = isTipOfTreeWebKitBuilder();
-    var revisionKey = shouldShowWebKitRevisions ? WEBKIT_REVISIONS_KEY : CHROME_REVISIONS_KEY;
-    var startRevision = results[revisionKey][numColumns - 1];
-    var endRevision = results[revisionKey][0];
-    var revisionLabel = shouldShowWebKitRevisions ? "WebKit Revision" : "Chromium Revision";
-
-    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
-    var html = chart("Total failing", {"": fixable}, revisionLabel, startRevision, endRevision);
-
-    var values = valuesPerExpectation(results[FIXABLE_COUNTS_KEY], numColumns);
-    // Don't care about number of passes for the charts.
-    delete(values['P']);
-
-    return html + chart("Detailed breakdown", values, revisionLabel, startRevision, endRevision);
-}
-
-var LABEL_COLORS = ['FF0000', '00FF00', '0000FF', '000000', 'FF6EB4', 'FFA812', '9B30FF', '00FFCC'];
-
-// FIXME: Find a better way to exclude outliers. This is just so we exclude
-// runs where every test failed.
-var MAX_VALUE = 10000;
-
-function filteredValues(values, desiredNumberOfPoints)
-{
-    // Filter out values to make the graph a bit more readable and to keep URLs
-    // from exceeding the browsers max length restriction.
-    var filterAmount = Math.floor(values.length / desiredNumberOfPoints);
-    if (filterAmount < 1)
-        return values;
-
-    return values.filter(function(element, index, array) {
-        // Include the most recent and oldest values and exclude outliers.
-        return (index % filterAmount == 0 || index == array.length - 1) && (array[index] < MAX_VALUE && array[index] != 0);
-    });
-}
-
-function chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints) {
-    var maxValue = 0;
-    for (var expectation in values)
-        maxValue = Math.max(maxValue, Math.max.apply(null, filteredValues(values[expectation], desiredNumberOfPoints)));
-
-    var chartData = '';
-    var labels = '';
-    var numLabels = 0;
-
-    var first = true;
-    for (var expectation in values) {
-        chartData += (first ? 'e:' : ',') + extendedEncode(filteredValues(values[expectation], desiredNumberOfPoints).reverse(), maxValue);
-
-        if (expectation) {
-            numLabels++;
-            labels += (first ? '' : '|') + expectationsMap()[expectation];
-        }
-        first = false;
-    }
-
-    var url = "http://chart.apis.google.com/chart?cht=lc&chs=600x400&chd=" +
-            chartData + "&chg=15,15,1,3&chxt=x,x,y&chxl=1:||" + revisionLabel +
-            "|&chxr=0," + startRevision + "," + endRevision + "|2,0," + maxValue + "&chtt=" + title;
-
-
-    if (labels)
-        url += "&chdl=" + labels + "&chco=" + LABEL_COLORS.slice(0, numLabels).join(',');
-    return url;
-}
-
-function chart(title, values, revisionLabel, startRevision, endRevision)
-{
-    var desiredNumberOfPoints = 400;
-    var url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
-
-    while (url.length >= 2048) {
-        // Decrease the desired number of points gradually until we get a URL that
-        // doesn't exceed chartserver's max URL length.
-        desiredNumberOfPoints = 3 / 4 * desiredNumberOfPoints;
-        url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
-    }
-
-    return '<img src="' + url + '">';
-}
-
-function htmlForRevisionRows(results, numColumns)
-{
-    return htmlForTableRow('WebKit Revision', results[WEBKIT_REVISIONS_KEY].slice(0, numColumns)) +
-        htmlForTableRow('Chrome Revision', results[CHROME_REVISIONS_KEY].slice(0, numColumns));
-}
-
-function wrapHTMLInTable(description, html)
-{
-    return '<h3>' + description + '</h3><table><tbody>' + html + '</tbody></table>';
-}
-
-function htmlForSummaryTable(results, numColumns)
-{
-    var percent = [];
-    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
-    var allFixable = results[ALL_FIXABLE_COUNT_KEY].slice(0, numColumns);
-    for (var i = 0; i < numColumns; i++) {
-        var percentage = 100 * (allFixable[i] - fixable[i]) / allFixable[i];
-        // Round to the nearest tenth of a percent.
-        percent.push(Math.round(percentage * 10) / 10 + '%');
-    }
-    var html = htmlForRevisionRows(results, numColumns) +
-        htmlForTableRow('Percent passed', percent) +
-        htmlForTableRow('Failures (deduped)', fixable) +
-        htmlForTableRow('Fixable Tests', allFixable);
-    return wrapHTMLInTable('Summary', html);
-}
-
-function valuesPerExpectation(counts, numColumns)
-{
-    var values = {};
-    for (var i = 0; i < numColumns; i++) {
-        for (var expectation in expectationsMap()) {
-            if (expectation in counts[i]) {
-                var count = counts[i][expectation];
-                if (!values[expectation])
-                    values[expectation] = [];
-                values[expectation].push(count);
-            }
-        }
-    }
-    return values;
-}
-
-function htmlForTestType(results, key, description, numColumns)
-{
-    var counts = results[key];
-    var html = htmlForRevisionRows(results, numColumns);
-    var values = valuesPerExpectation(counts, numColumns);
-    for (var expectation in values)
-        html += htmlForTableRow(expectationsMap()[expectation], values[expectation]);
-    return wrapHTMLInTable(description, html);
-}
-
-function htmlForTableRow(columnName, values)
-{
-    return '<tr><td>' + columnName + '</td><td>' + values.join('</td><td>') + '</td></tr>';
-}
-
-// Taken from http://code.google.com/apis/chart/docs/data_formats.html.
-function extendedEncode(arrVals, maxVal)
-{
-    var map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
-    var mapLength = map.length;
-    var mapLengthSquared = mapLength * mapLength;
-
-    var chartData = '';
-
-    for (var i = 0, len = arrVals.length; i < len; i++) {
-        // In case the array vals were translated to strings.
-        var numericVal = new Number(arrVals[i]);
-        // Scale the value to maxVal.
-        var scaledVal = Math.floor(mapLengthSquared * numericVal / maxVal);
-
-        if(scaledVal > mapLengthSquared - 1)
-            chartData += "..";
-        else if (scaledVal < 0)
-            chartData += '__';
-        else {
-            // Calculate first and second digits and add them to the output.
-            var quotient = Math.floor(scaledVal / mapLength);
-            var remainder = scaledVal - mapLength * quotient;
-            chartData += map.charAt(quotient) + map.charAt(remainder);
-        }
-    }
-
-    return chartData;
-}
-</script>
+<script src="ui.js"></script>
+<script src="aggregate_results.js"></script>
 </head>
 <body></body>
 </html>
diff --git a/Tools/TestResultServer/static-dashboards/aggregate_results.js b/Tools/TestResultServer/static-dashboards/aggregate_results.js
new file mode 100644
index 0000000..0fd2dfe
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/aggregate_results.js
@@ -0,0 +1,272 @@
+// Copyright (C) 2013 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.
+//
+// @fileoverview Creates a dashboard for tracking number of passes/failures per run.
+//
+// Currently, only webkit tests are supported, but adding other test types
+// should just require the following steps:
+//     -generate results.json for these tests
+//     -copy them to the appropriate location
+//     -add the builder name to the list of builders in dashboard_base.js.
+
+//////////////////////////////////////////////////////////////////////////////
+// Methods and objects from dashboard_base.js to override.
+//////////////////////////////////////////////////////////////////////////////
+function generatePage()
+{
+    var html = ui.html.testTypeSwitcher(true) + '<br>';
+    for (var builder in currentBuilders())
+        html += htmlForBuilder(builder);
+    document.body.innerHTML = html;
+}
+
+function handleValidHashParameter(key, value)
+{
+    switch(key) {
+    case 'rawValues':
+        g_history.dashboardSpecificState[key] = value == 'true';
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+g_defaultDashboardSpecificStateValues = {
+    rawValues: false
+};
+
+function htmlForBuilder(builder)
+{
+    var results = g_resultsByBuilder[builder];
+    // Some keys were added later than others, so they don't have as many
+    // builds. Use the shortest.
+    // FIXME: Once 500 runs have finished, we can get rid of passing this
+    // around and just assume all keys have the same number of builders for a
+    // given builder.
+    var numColumns = results[ALL_FIXABLE_COUNT_KEY].length;
+    var html = '<div class=container><h2>' + builder + '</h2>';
+
+    if (g_history.dashboardSpecificState.rawValues)
+        html += rawValuesHTML(results, numColumns);
+    else {
+        html += '<a href="timeline_explorer.html' + (location.hash ? location.hash + '&' : '#') + 'builder=' + builder + '">' +
+            chartHTML(results, numColumns) + '</a>';
+    }
+
+    html += '</div>';
+    return html;
+}
+
+function rawValuesHTML(results, numColumns)
+{
+    var html = htmlForSummaryTable(results, numColumns) +
+        htmlForTestType(results, FIXABLE_COUNTS_KEY, FIXABLE_DESCRIPTION, numColumns);
+    if (g_history.isLayoutTestResults()) {
+        html += htmlForTestType(results, DEFERRED_COUNTS_KEY, DEFERRED_DESCRIPTION, numColumns) +
+            htmlForTestType(results, WONTFIX_COUNTS_KEY, WONTFIX_DESCRIPTION, numColumns);
+    }
+    return html;
+}
+
+function chartHTML(results, numColumns)
+{
+    var shouldShowWebKitRevisions = isTipOfTreeWebKitBuilder();
+    var revisionKey = shouldShowWebKitRevisions ? WEBKIT_REVISIONS_KEY : CHROME_REVISIONS_KEY;
+    var startRevision = results[revisionKey][numColumns - 1];
+    var endRevision = results[revisionKey][0];
+    var revisionLabel = shouldShowWebKitRevisions ? "WebKit Revision" : "Chromium Revision";
+
+    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
+    var html = chart("Total failing", {"": fixable}, revisionLabel, startRevision, endRevision);
+
+    var values = valuesPerExpectation(results[FIXABLE_COUNTS_KEY], numColumns);
+    // Don't care about number of passes for the charts.
+    delete(values['P']);
+
+    return html + chart("Detailed breakdown", values, revisionLabel, startRevision, endRevision);
+}
+
+var LABEL_COLORS = ['FF0000', '00FF00', '0000FF', '000000', 'FF6EB4', 'FFA812', '9B30FF', '00FFCC'];
+
+// FIXME: Find a better way to exclude outliers. This is just so we exclude
+// runs where every test failed.
+var MAX_VALUE = 10000;
+
+function filteredValues(values, desiredNumberOfPoints)
+{
+    // Filter out values to make the graph a bit more readable and to keep URLs
+    // from exceeding the browsers max length restriction.
+    var filterAmount = Math.floor(values.length / desiredNumberOfPoints);
+    if (filterAmount < 1)
+        return values;
+
+    return values.filter(function(element, index, array) {
+        // Include the most recent and oldest values and exclude outliers.
+        return (index % filterAmount == 0 || index == array.length - 1) && (array[index] < MAX_VALUE && array[index] != 0);
+    });
+}
+
+function chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints) {
+    var maxValue = 0;
+    for (var expectation in values)
+        maxValue = Math.max(maxValue, Math.max.apply(null, filteredValues(values[expectation], desiredNumberOfPoints)));
+
+    var chartData = '';
+    var labels = '';
+    var numLabels = 0;
+
+    var first = true;
+    for (var expectation in values) {
+        chartData += (first ? 'e:' : ',') + extendedEncode(filteredValues(values[expectation], desiredNumberOfPoints).reverse(), maxValue);
+
+        if (expectation) {
+            numLabels++;
+            labels += (first ? '' : '|') + expectationsMap()[expectation];
+        }
+        first = false;
+    }
+
+    var url = "http://chart.apis.google.com/chart?cht=lc&chs=600x400&chd=" +
+            chartData + "&chg=15,15,1,3&chxt=x,x,y&chxl=1:||" + revisionLabel +
+            "|&chxr=0," + startRevision + "," + endRevision + "|2,0," + maxValue + "&chtt=" + title;
+
+
+    if (labels)
+        url += "&chdl=" + labels + "&chco=" + LABEL_COLORS.slice(0, numLabels).join(',');
+    return url;
+}
+
+function chart(title, values, revisionLabel, startRevision, endRevision)
+{
+    var desiredNumberOfPoints = 400;
+    var url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
+
+    while (url.length >= 2048) {
+        // Decrease the desired number of points gradually until we get a URL that
+        // doesn't exceed chartserver's max URL length.
+        desiredNumberOfPoints = 3 / 4 * desiredNumberOfPoints;
+        url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
+    }
+
+    return '<img src="' + url + '">';
+}
+
+function htmlForRevisionRows(results, numColumns)
+{
+    return htmlForTableRow('WebKit Revision', results[WEBKIT_REVISIONS_KEY].slice(0, numColumns)) +
+        htmlForTableRow('Chrome Revision', results[CHROME_REVISIONS_KEY].slice(0, numColumns));
+}
+
+function wrapHTMLInTable(description, html)
+{
+    return '<h3>' + description + '</h3><table><tbody>' + html + '</tbody></table>';
+}
+
+function htmlForSummaryTable(results, numColumns)
+{
+    var percent = [];
+    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
+    var allFixable = results[ALL_FIXABLE_COUNT_KEY].slice(0, numColumns);
+    for (var i = 0; i < numColumns; i++) {
+        var percentage = 100 * (allFixable[i] - fixable[i]) / allFixable[i];
+        // Round to the nearest tenth of a percent.
+        percent.push(Math.round(percentage * 10) / 10 + '%');
+    }
+    var html = htmlForRevisionRows(results, numColumns) +
+        htmlForTableRow('Percent passed', percent) +
+        htmlForTableRow('Failures (deduped)', fixable) +
+        htmlForTableRow('Fixable Tests', allFixable);
+    return wrapHTMLInTable('Summary', html);
+}
+
+function valuesPerExpectation(counts, numColumns)
+{
+    var values = {};
+    for (var i = 0; i < numColumns; i++) {
+        for (var expectation in expectationsMap()) {
+            if (expectation in counts[i]) {
+                var count = counts[i][expectation];
+                if (!values[expectation])
+                    values[expectation] = [];
+                values[expectation].push(count);
+            }
+        }
+    }
+    return values;
+}
+
+function htmlForTestType(results, key, description, numColumns)
+{
+    var counts = results[key];
+    var html = htmlForRevisionRows(results, numColumns);
+    var values = valuesPerExpectation(counts, numColumns);
+    for (var expectation in values)
+        html += htmlForTableRow(expectationsMap()[expectation], values[expectation]);
+    return wrapHTMLInTable(description, html);
+}
+
+function htmlForTableRow(columnName, values)
+{
+    return '<tr><td>' + columnName + '</td><td>' + values.join('</td><td>') + '</td></tr>';
+}
+
+// Taken from http://code.google.com/apis/chart/docs/data_formats.html.
+function extendedEncode(arrVals, maxVal)
+{
+    var map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
+    var mapLength = map.length;
+    var mapLengthSquared = mapLength * mapLength;
+
+    var chartData = '';
+
+    for (var i = 0, len = arrVals.length; i < len; i++) {
+        // In case the array vals were translated to strings.
+        var numericVal = new Number(arrVals[i]);
+        // Scale the value to maxVal.
+        var scaledVal = Math.floor(mapLengthSquared * numericVal / maxVal);
+
+        if(scaledVal > mapLengthSquared - 1)
+            chartData += "..";
+        else if (scaledVal < 0)
+            chartData += '__';
+        else {
+            // Calculate first and second digits and add them to the output.
+            var quotient = Math.floor(scaledVal / mapLength);
+            var remainder = scaledVal - mapLength * quotient;
+            chartData += map.charAt(quotient) + map.charAt(remainder);
+        }
+    }
+
+    return chartData;
+}
+
+window.addEventListener('load', function() {
+    var resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);
diff --git a/Tools/TestResultServer/static-dashboards/builders.js b/Tools/TestResultServer/static-dashboards/builders.js
index 2a6b67d..39000ce 100644
--- a/Tools/TestResultServer/static-dashboards/builders.js
+++ b/Tools/TestResultServer/static-dashboards/builders.js
@@ -29,31 +29,72 @@
 // @fileoverview File that lists builders, their masters, and logical groupings
 // of them.
 
-function BuilderMaster(name, basePath)
+function LOAD_BUILDBOT_DATA(builderData)
+{
+    builders.masters = {};
+    builderData.forEach(function(master) {
+        builders.masters[master.name] = new builders.BuilderMaster(master.name, master.url, master.tests);
+    })
+}
+
+var builders = builders || {};
+
+(function() {
+
+// FIXME: Move some of this loading logic into loader.js.
+
+builders._loadScript = function(url, success, error)
+{
+    var script = document.createElement('script');
+    script.src = url;
+    script.onload = success;
+    script.onerror = error;
+    document.head.appendChild(script);
+}
+
+builders._requestBuilders = function()
+{
+    var buildersUrl = 'builders.jsonp';
+    builders._loadScript(buildersUrl, function() {}, function() {
+        console.error('Could not load ' + buildersUrl);
+    });
+}
+
+
+builders.BuilderMaster = function(name, basePath, tests)
 {
     this.name = name;
     this.basePath = basePath;
+    this.tests = tests;
 }
 
-BuilderMaster.prototype.logPath = function(builder, buildNumber)
-{
-    return this.basePath + 'builders/' + builder + '/builds/' + buildNumber;
-};
+builders.BuilderMaster.prototype = {
+    logPath: function(builder, buildNumber)
+    {
+        return this.basePath + '/builders/' + builder + '/builds/' + buildNumber;
+    },
+    builderJsonPath: function()
+    {
+        return this.basePath + '/json/builders';
+    },
+}
 
-BuilderMaster.prototype.builderJsonPath = function()
-{
-    return this.basePath + 'json/builders';
-};
+builders._requestBuilders();
 
-CHROMIUM_WIN_BUILDER_MASTER = new BuilderMaster('ChromiumWin', 'http://build.chromium.org/p/chromium.win/');
-CHROMIUM_MAC_BUILDER_MASTER = new BuilderMaster('ChromiumMac', 'http://build.chromium.org/p/chromium.mac/');
-CHROMIUM_LINUX_BUILDER_MASTER = new BuilderMaster('ChromiumLinux', 'http://build.chromium.org/p/chromium.linux/');
-CHROMIUMOS_BUILDER_MASTER = new BuilderMaster('ChromiumChromiumOS', 'http://build.chromium.org/p/chromium.chromiumos/');
-CHROMIUM_GPU_BUILDER_MASTER = new BuilderMaster('ChromiumGPU', 'http://build.chromium.org/p/chromium.gpu/');
-CHROMIUM_GPU_FYI_BUILDER_MASTER = new BuilderMaster('ChromiumGPUFYI', 'http://build.chromium.org/p/chromium.gpu.fyi/');
-CHROMIUM_PERF_AV_BUILDER_MASTER = new BuilderMaster('ChromiumPerfAv', 'http://build.chromium.org/p/chromium.perf_av/');
-CHROMIUM_WEBKIT_BUILDER_MASTER = new BuilderMaster('ChromiumWebkit', 'http://build.chromium.org/p/chromium.webkit/');
-WEBKIT_BUILDER_MASTER = new BuilderMaster('webkit.org', 'http://build.webkit.org/');
+})();
+
+// FIXME: Move everything below into the anonymous namespace above.
+
+CHROMIUM_WIN_BUILDER_MASTER = 'ChromiumWin';
+CHROMIUM_MAC_BUILDER_MASTER = 'ChromiumMac';
+CHROMIUM_LINUX_BUILDER_MASTER = 'ChromiumLinux';
+CHROMIUMOS_BUILDER_MASTER = 'ChromiumChromiumOS';
+CHROMIUM_GPU_BUILDER_MASTER = 'ChromiumGPU';
+CHROMIUM_GPU_FYI_BUILDER_MASTER = 'ChromiumGPUFYI';
+CHROMIUM_FYI_BUILDER_MASTER = 'ChromiumFYI';
+CHROMIUM_PERF_AV_BUILDER_MASTER = 'ChromiumPerfAv';
+CHROMIUM_WEBKIT_BUILDER_MASTER = 'ChromiumWebkit';
+WEBKIT_BUILDER_MASTER = 'webkit.org';
 
 var LEGACY_BUILDER_MASTERS_TO_GROUPS = {
     'Chromium': '@DEPS - chromium.org',
@@ -74,88 +115,48 @@
     // Map of builderName (the name shown in the waterfall) to builderPath (the
     // path used in the builder's URL)
     this.builders = {};
-    this.groups = 0;
-    this.expectedGroups = 0;
 }
 
-BuilderGroup.prototype.setbuilder = function(builder, flags) {
-    this.builders[builder] = builder.replace(/[ .()]/g, '_');
-    // FIXME: Remove this at some point, we don't actually use DEFAULT_BUILDER
-    //        in any meaningful way anymore.  We always just default to the
-    //        first builder in alphabetical order.
-    if (flags & BuilderGroup.DEFAULT_BUILDER)
-        this.defaultBuilder = builder;
-};
-
 BuilderGroup.prototype.append = function(builders) {
-    builders.forEach(function(builderAndFlags) {
-        var builder = builderAndFlags[0];
-        var flags = builderAndFlags[1];
-        this.setbuilder(builder, flags);
+    builders.forEach(function(builderName) {
+        this.builders[builderName] = builderName.replace(/[ .()]/g, '_');
     }, this);
-    this.groups += 1;
 };
 
-BuilderGroup.prototype.loaded = function() {
-    return this.groups >= this.expectedGroups;
+BuilderGroup.prototype.defaultBuilder = function()
+{
+    for (var builder in this.builders)
+        return builder;
+    console.error('There are no builders in this builder group.');
 }
 
-BuilderGroup.prototype.setup = function()
+BuilderGroup.prototype.master = function()
 {
-    // FIXME: instead of copying these to globals, it would be better if
-    // the rest of the code read things from the BuilderGroup instance directly
-    g_defaultBuilderName = this.defaultBuilder;
-    g_builders = this.builders;
-};
+    return builderMaster(this.defaultBuilder());
+}
 
 BuilderGroup.TOT_WEBKIT = true;
 BuilderGroup.DEPS_WEBKIT = false;
-BuilderGroup.DEFAULT_BUILDER = 1 << 1;
 
 var BUILDER_TO_MASTER = {};
-function associateBuildersWithMaster(builders, master)
+
+function builderMaster(builderName)
 {
-    builders.forEach(function(builderAndFlags) {
-        var builder = builderAndFlags[0];
-        BUILDER_TO_MASTER[builder] = master;
-    });
+    return BUILDER_TO_MASTER[builderName];
 }
 
-function requestBuilderList(builderGroups, builderFilter, master, groupName, builderGroup)
+function requestBuilderList(builderGroups, masterName, groupName, builderGroup, testType, opt_builderFilter)
 {
     if (!builderGroups[groupName])
         builderGroups[groupName] = builderGroup;
-    loader.request(master.builderJsonPath(),
-                   partial(onBuilderListLoad, builderGroups, builderFilter, master, groupName),
-                   partial(onErrorLoadingBuilderList, master.builderJsonPath(), builderGroups, groupName));
-    builderGroups[groupName].expectedGroups += 1;
-}
-
-function isChromiumDepsGpuTestRunner(builder)
-{
-    return true;
-}
-
-function isChromiumDepsFyiGpuTestRunner(builder)
-{
-    // FIXME: This is kind of wonky, but there's not really a better pattern.
-    return builder.indexOf('(') != -1;
-}
-
-function isChromiumTipOfTreeGpuTestRunner(builder)
-{
-    return builder.indexOf('GPU') != -1;
-}
-
-function isWebkitTestRunner(builder)
-{
-    if (builder.indexOf('EFL') != -1)
-        return builder.indexOf('Build') == -1;
-    if (builder.indexOf('Tests') != -1) {
-        // Apple Windows bots still run old-run-webkit-tests, so they don't upload data.
-        return builder.indexOf('Win') == -1 || (builder.indexOf('Qt') != -1 && builder.indexOf('Chromium') != -1);
-    }
-    return builder.indexOf('GTK') != -1 || builder == 'Qt Linux Release';
+    var master = builders.masters[masterName];
+    var builderList = master.tests[testType].builders;
+    if (opt_builderFilter)
+        builderList = builderList.filter(opt_builderFilter);
+    builderList.forEach(function(builderName) {
+        BUILDER_TO_MASTER[builderName] = master;
+    });
+    builderGroups[groupName].append(builderList);
 }
 
 function isChromiumContentShellTestRunner(builder)
@@ -166,61 +167,15 @@
 function isChromiumWebkitTipOfTreeTestRunner(builder)
 {
     // FIXME: Remove the Android check once the android tests bot is actually uploading results.
-    return builder.indexOf('WebKit') != -1 && builder.indexOf('Builder') == -1 && builder.indexOf('(deps)') == -1 &&
-        builder.indexOf('ASAN') == -1 && !isChromiumContentShellTestRunner(builder) && builder.indexOf('Android') == -1;
+    return builder.indexOf('ASAN') == -1 && !isChromiumContentShellTestRunner(builder) && builder.indexOf('Android') == -1 && !isChromiumWebkitDepsTestRunner(builder);
 }
 
 function isChromiumWebkitDepsTestRunner(builder)
 {
-    return builder.indexOf('WebKit') != -1 && builder.indexOf('Builder') == -1 && builder.indexOf('(deps)') != -1;
+    return builder.indexOf('(deps)') != -1;
 }
 
-function isChromiumDepsGTestRunner(builder)
-{
-    return builder.indexOf('Builder') == -1;
-}
-
-function isChromiumDepsCrosGTestRunner(builder)
-{
-    return builder.indexOf('Tests') != -1;
-}
-
-function isChromiumTipOfTreeGTestRunner(builder)
-{
-    return !isChromiumTipOfTreeGpuTestRunner(builder) && builder.indexOf('Builder') == -1 && builder.indexOf('Perf') == -1 &&
-         builder.indexOf('WebKit') == -1 && builder.indexOf('Valgrind') == -1 && builder.indexOf('Chrome Frame') == -1;
-}
-
-function isChromiumDepsAVTestRunner(builder)
-{
-    return builder.indexOf('Builder') == -1;
-}
-
-function generateBuildersFromBuilderList(builderList, filter)
-{
-    return builderList.filter(filter).map(function(tester, index) {
-        var builder = [tester];
-        if (!index)
-            builder.push(BuilderGroup.DEFAULT_BUILDER);
-        return builder;
-    });
-}
-
-function onBuilderListLoad(builderGroups, builderFilter, master, groupName, xhr)
-{
-    var builders = generateBuildersFromBuilderList(Object.keys(JSON.parse(xhr.responseText)), builderFilter);
-    associateBuildersWithMaster(builders, master);
-    builderGroups[groupName].append(builders);
-    if (builderGroups[groupName].loaded())
-        g_resourceLoader.buildersListLoaded();
-}
-
-function onErrorLoadingBuilderList(url, builderGroups, groupName, xhr)
-{
-    builderGroups[groupName].groups += 1;
-    console.log('Could not load list of builders from ' + url + '. Try reloading.');
-}
-
+// FIXME: Look into whether we can move the grouping logic into builders.jsonp and get rid of this code.
 function loadBuildersList(groupName, testType) {
     switch (testType) {
     case 'gl_tests':
@@ -228,17 +183,17 @@
         switch(groupName) {
         case '@DEPS - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, isChromiumDepsGpuTestRunner, CHROMIUM_GPU_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, CHROMIUM_GPU_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
 
         case '@DEPS FYI - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, isChromiumDepsFyiGpuTestRunner, CHROMIUM_GPU_FYI_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, CHROMIUM_GPU_FYI_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
 
         case '@ToT - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, isChromiumTipOfTreeGpuTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GPU_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
         }
         break;
@@ -247,60 +202,91 @@
         switch(groupName) {
         case 'Content Shell @ToT - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumContentShellTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType, isChromiumContentShellTestRunner);
             break;
 
         case '@ToT - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumWebkitTipOfTreeTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType, isChromiumWebkitTipOfTreeTestRunner);
             break;
 
         case '@ToT - webkit.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, isWebkitTestRunner, WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
 
         case '@DEPS - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumWebkitDepsTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
-            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumDepsAVTestRunner, CHROMIUM_PERF_AV_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType, isChromiumWebkitDepsTestRunner);
+            requestBuilderList(LAYOUT_TESTS_BUILDER_GROUPS, CHROMIUM_PERF_AV_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
         }
         break;
-   
+
     case 'test_shell_tests':
     case 'webkit_unit_tests':
         switch(groupName) {
         case '@ToT - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(TEST_SHELL_TESTS_BUILDER_GROUPS, isChromiumWebkitTipOfTreeTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(TEST_SHELL_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType, isChromiumWebkitTipOfTreeTestRunner);
             break;
 
         case '@DEPS - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(TEST_SHELL_TESTS_BUILDER_GROUPS, isChromiumWebkitDepsTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
-            requestBuilderList(TEST_SHELL_TESTS_BUILDER_GROUPS, isChromiumDepsAVTestRunner, CHROMIUM_PERF_AV_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(TEST_SHELL_TESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType, isChromiumWebkitDepsTestRunner);
             break;
         }
-        break;    
+        break;
+
+    case 'androidwebview_instrumentation_tests':
+    case 'chromiumtestshell_instrumentation_tests':
+    case 'contentshell_instrumentation_tests':
+        switch(groupName) {
+        case '@DEPS - chromium.org':
+            var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
+            requestBuilderList(CHROMIUM_INSTRUMENTATION_TESTS_BUILDER_GROUPS, CHROMIUM_LINUX_BUILDER_MASTER, groupName, builderGroup, testType);
+            break;
+
+        case '@DEPS FYI - chromium.org':
+            var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
+            requestBuilderList(CHROMIUM_INSTRUMENTATION_TESTS_BUILDER_GROUPS, CHROMIUM_FYI_BUILDER_MASTER , groupName, builderGroup, testType);
+            break;
+        }
+        break;
+
+    case 'cc_unittests':    
+        switch(groupName) {
+        case '@DEPS - chromium.org':
+            var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
+            requestBuilderList(CC_UNITTEST_BUILDER_GROUPS, CHROMIUM_WIN_BUILDER_MASTER, groupName, builderGroup, testType);
+            requestBuilderList(CC_UNITTEST_BUILDER_GROUPS, CHROMIUM_MAC_BUILDER_MASTER, groupName, builderGroup, testType);
+            requestBuilderList(CC_UNITTEST_BUILDER_GROUPS, CHROMIUM_LINUX_BUILDER_MASTER, groupName, builderGroup, testType);
+            break;
+
+        case '@ToT - chromium.org':
+            var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
+            requestBuilderList(CC_UNITTEST_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType);
+            break;
+        }
+        break;
 
     default:
         switch(groupName) {
         case '@DEPS - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, isChromiumDepsGTestRunner, CHROMIUM_WIN_BUILDER_MASTER, groupName, builderGroup);
-            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, isChromiumDepsGTestRunner, CHROMIUM_MAC_BUILDER_MASTER, groupName, builderGroup);
-            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, isChromiumDepsGTestRunner, CHROMIUM_LINUX_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, CHROMIUM_WIN_BUILDER_MASTER, groupName, builderGroup, testType);
+            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, CHROMIUM_MAC_BUILDER_MASTER, groupName, builderGroup, testType);
+            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, CHROMIUM_LINUX_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
 
         case '@DEPS CrOS - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.DEPS_WEBKIT);
-            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, isChromiumDepsCrosGTestRunner, CHROMIUMOS_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, CHROMIUMOS_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
 
         case '@ToT - chromium.org':
             var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, isChromiumTipOfTreeGTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup);
+            requestBuilderList(CHROMIUM_GTESTS_BUILDER_GROUPS, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, builderGroup, testType);
             break;
         }
         break;
@@ -330,3 +316,13 @@
     '@DEPS CrOS - chromium.org': null,
     '@ToT - chromium.org': null,
 };
+
+var CHROMIUM_INSTRUMENTATION_TESTS_BUILDER_GROUPS = {
+    '@DEPS - chromium.org': null,
+    '@DEPS FYI - chromium.org': null,
+};
+
+var CC_UNITTEST_BUILDER_GROUPS = {
+    '@ToT - chromium.org': null,
+    '@DEPS - chromium.org': null,
+};
\ No newline at end of file
diff --git a/Tools/TestResultServer/static-dashboards/builders.jsonp b/Tools/TestResultServer/static-dashboards/builders.jsonp
new file mode 100644
index 0000000..91d61eb
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/builders.jsonp
@@ -0,0 +1,3034 @@
+// This file is auto-generated by Tools/TestResultServer/generate_builders_json.py. It should not be manually modified.
+// It uses jsonp instead of proper json because we want to be able to load it from a file URL in Chrome for local testing.
+LOAD_BUILDBOT_DATA([
+    {
+        "name": "ChromiumWin", 
+        "tests": {
+            "ash_unittests": {
+                "builders": [
+                    "Win8 Aura", 
+                    "Win Aura Tests (1)"
+                ]
+            }, 
+            "aura_unittests": {
+                "builders": [
+                    "Win8 Aura", 
+                    "Win Aura Tests (1)"
+                ]
+            }, 
+            "base_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "XP Tests (2)", 
+                    "Win7 Tests (2)", 
+                    "Vista Tests (2)", 
+                    "Win 7 Tests x64 (2)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "XP Tests (dbg)(5)", 
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(3)", 
+                    "Win7 Tests (dbg)(5)", 
+                    "XP Tests (3)", 
+                    "XP Tests (dbg)(4)", 
+                    "Vista Tests (1)", 
+                    "Win Aura Tests (2)", 
+                    "Vista Tests (3)", 
+                    "XP Tests (2)", 
+                    "XP Tests (dbg)(2)", 
+                    "Win7 Tests (dbg)(6)", 
+                    "XP Tests (dbg)(6)", 
+                    "Win7 Tests (2)", 
+                    "Win7 Tests (dbg)(4)", 
+                    "Win7 Tests (3)", 
+                    "Win7 Tests (dbg)(2)", 
+                    "Vista Tests (2)", 
+                    "Win7 Tests (1)", 
+                    "Win Aura Tests (3)", 
+                    "Win Aura Tests (1)", 
+                    "XP Tests (dbg)(3)"
+                ]
+            }, 
+            "cacheinvalidation_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "cc_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "chrome_frame_net_tests": {
+                "builders": [
+                    "Chrome Frame Tests (ie8)", 
+                    "Chrome Frame Tests (ie6)", 
+                    "Chrome Frame Tests (ie7)", 
+                    "Chrome Frame Tests (ie9)"
+                ]
+            }, 
+            "chrome_frame_tests": {
+                "builders": [
+                    "Chrome Frame Tests (ie8)", 
+                    "Chrome Frame Tests (ie6)", 
+                    "Chrome Frame Tests (ie7)", 
+                    "Chrome Frame Tests (ie9)"
+                ]
+            }, 
+            "chrome_frame_unittests": {
+                "builders": [
+                    "Chrome Frame Tests (ie8)", 
+                    "Chrome Frame Tests (ie6)", 
+                    "Chrome Frame Tests (ie7)", 
+                    "Chrome Frame Tests (ie9)"
+                ]
+            }, 
+            "chromedriver2_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "XP Tests (3)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "compositor_unittests": {
+                "builders": [
+                    "Win Aura Tests (2)", 
+                    "Win8 Aura"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Vista Tests (1)", 
+                    "XP Tests (dbg)(2)", 
+                    "Win7 Tests (dbg)(2)", 
+                    "Win7 Tests (1)", 
+                    "Win Aura Tests (1)"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "XP Tests (3)", 
+                    "Win Aura Tests (2)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "courgette_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "crypto_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "device_unittests": {
+                "builders": [
+                    "Win 7 Tests x64 (1)"
+                ]
+            }, 
+            "googleurl_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "installer_util_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Interactive Tests (dbg)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "Win Aura Tests (3)"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (3)", 
+                    "XP Tests (3)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "jingle_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "mini_installer_test": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "XP Tests (2)", 
+                    "XP Tests (dbg)(2)", 
+                    "Win7 Tests (2)", 
+                    "Win7 Tests (dbg)(2)", 
+                    "Vista Tests (2)", 
+                    "Win 7 Tests x64 (2)"
+                ]
+            }, 
+            "ppapi_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "printing_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "remoting_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "sbox_integration_tests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(3)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(3)"
+                ]
+            }, 
+            "sbox_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(3)", 
+                    "Win 7 Tests x64 (1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(3)"
+                ]
+            }, 
+            "sbox_validation_tests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(3)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(3)"
+                ]
+            }, 
+            "sbox_validation_tests_buildrunner_ignore": {
+                "builders": [
+                    "Win 7 Tests x64 (1)"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (3)", 
+                    "XP Tests (3)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "sync_integration_tests": {
+                "builders": [
+                    "Win7 Sync"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (3)", 
+                    "XP Tests (3)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (3)", 
+                    "XP Tests (3)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "XP Tests (3)", 
+                    "Win Aura Tests (2)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "views_unittests": {
+                "builders": [
+                    "Win7 Tests (dbg)(1)", 
+                    "Win 7 Tests x64 (3)", 
+                    "XP Tests (3)", 
+                    "Win Aura Tests (2)", 
+                    "Vista Tests (3)", 
+                    "Win7 Tests (3)", 
+                    "Win8 Aura", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }, 
+            "webkit_compositor_bindings_unittests": {
+                "builders": [
+                    "XP Tests (1)", 
+                    "Win7 Tests (dbg)(1)", 
+                    "Vista Tests (1)", 
+                    "Win7 Tests (1)", 
+                    "XP Tests (dbg)(1)"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.win"
+    }, 
+    {
+        "name": "ChromiumMac", 
+        "tests": {
+            "base_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.7 Tests (dbg)(3)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(3)"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (3)", 
+                    "Mac 10.7 Tests (dbg)(3)", 
+                    "Mac 10.7 Tests (dbg)(2)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)", 
+                    "Mac 10.6 Tests (dbg)(2)", 
+                    "Mac 10.6 Tests (dbg)(3)", 
+                    "Mac10.7 Tests (3)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "cacheinvalidation_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "cc_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "chromedriver2_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "crypto_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "googleurl_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(3)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(3)"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "jingle_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.7 Tests (dbg)(2)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(2)"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "Mac10.6 Tests (3)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.7 Tests (dbg)(2)", 
+                    "Mac 10.6 Tests (dbg)(2)", 
+                    "Mac10.7 Tests (3)"
+                ]
+            }, 
+            "ppapi_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "printing_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "remoting_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "sync_integration_tests": {
+                "builders": [
+                    "Mac10.6 Sync"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Mac10.6 Tests (2)", 
+                    "iOS Simulator (dbg)", 
+                    "Mac 10.6 Tests (dbg)(4)", 
+                    "Mac10.7 Tests (2)", 
+                    "Mac 10.7 Tests (dbg)(4)"
+                ]
+            }, 
+            "webkit_compositor_bindings_unittests": {
+                "builders": [
+                    "Mac10.7 Tests (1)", 
+                    "Mac 10.7 Tests (dbg)(1)", 
+                    "Mac10.6 Tests (1)", 
+                    "Mac 10.6 Tests (dbg)(1)"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.mac"
+    }, 
+    {
+        "name": "ChromiumLinux", 
+        "tests": {
+            "Run tests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "android_webview_unittests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "androidwebview_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "aura_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura"
+                ]
+            }, 
+            "base_unittests": {
+                "builders": [
+                    "Linux Precise (dbg)", 
+                    "Linux Aura (Precise)", 
+                    "Linux Precise x64", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "Linux Precise (dbg)", 
+                    "Linux Tests (dbg)(1)(Precise 32)", 
+                    "Linux Aura (Precise)", 
+                    "Linux Precise x64", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(1)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(1)(Precise)", 
+                    "Linux Tests x64"
+                ]
+            }, 
+            "cacheinvalidation_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "cc_unittests": {
+                "builders": [
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "chromedriver2_unittests": {
+                "builders": [
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "chromiumtestshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Linux Clang (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "compositor_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "Linux Precise (dbg)", 
+                    "Linux Tests (dbg)(1)(Precise 32)", 
+                    "Linux Aura (Precise)", 
+                    "Linux Precise x64", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(1)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(1)(Precise)", 
+                    "Linux Tests x64"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "contentshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "crypto_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "dbus_unittests": {
+                "builders": [
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "device_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "googleurl_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "jingle_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "Linux Tests (dbg)(1)(Precise 32)", 
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(1)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(1)(Precise)", 
+                    "Linux Tests x64", 
+                    "Android Tests"
+                ]
+            }, 
+            "ppapi_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "printing_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "remoting_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "sandbox_linux_unittests": {
+                "builders": [
+                    "Linux Precise (dbg)", 
+                    "Linux Aura (Precise)", 
+                    "Linux Precise x64", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "sync_integration_tests": {
+                "builders": [
+                    "Linux Sync (Precise)", 
+                    "Linux Sync"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "test_report": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Clang (dbg)", 
+                    "Linux Aura", 
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Clang (dbg)(Precise)", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "views_unittests": {
+                "builders": [
+                    "Linux Aura (Precise)", 
+                    "Linux Aura"
+                ]
+            }, 
+            "webkit_compositor_bindings_unittests": {
+                "builders": [
+                    "Linux Tests (dbg)(2)(Precise 32)", 
+                    "Linux Tests (Precise)", 
+                    "Android Tests (dbg)", 
+                    "Linux Tests (dbg)(2)(Precise)", 
+                    "Linux Tests x64", 
+                    "Linux Tests (dbg)(2)", 
+                    "Android Tests"
+                ]
+            }, 
+            "webkit_unit_tests": {
+                "builders": [
+                    "Android Tests (dbg)", 
+                    "Android Tests"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.linux"
+    }, 
+    {
+        "name": "ChromiumChromiumOS", 
+        "tests": {
+            "app_list_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "ash_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "aura_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "base_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (2)", 
+                    "Linux ChromiumOS Tests (2)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(2)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(2)"
+                ]
+            }, 
+            "cacheinvalidation_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "chromeos_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "compositor_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (2)", 
+                    "Linux ChromiumOS Tests (2)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(2)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(2)"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "crypto_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "dbus_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "device_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "googleurl_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(3)", 
+                    "Linux ChromiumOS Tests (2)", 
+                    "Linux ChromiumOS Tests (dbg)(3)(Precise)", 
+                    "Linux ChromiumOS Tests (2)(Precise)"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "jingle_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "message_center_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "ppapi_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "printing_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "remoting_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "sandbox_linux_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }, 
+            "views_unittests": {
+                "builders": [
+                    "Linux ChromiumOS Tests (dbg)(1)", 
+                    "Linux ChromiumOS Tests (1)", 
+                    "Linux ChromiumOS Tests (1)(Precise)", 
+                    "Linux ChromiumOS Tests (dbg)(1)(Precise)"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.chromiumos"
+    }, 
+    {
+        "name": "ChromiumGPU", 
+        "tests": {
+            "content_browsertests": {
+                "builders": [
+                    "Mac 10.8 Debug (Intel)", 
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Win7 Debug (NVIDIA)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac Debug (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Linux Debug (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Mac Retina Debug", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gl_tests": {
+                "builders": [
+                    "Mac 10.8 Debug (Intel)", 
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Win7 Debug (NVIDIA)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac Debug (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Linux Debug (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Mac Retina Debug", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gles2_conform_test": {
+                "builders": [
+                    "Mac 10.8 Debug (Intel)", 
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Win7 Debug (NVIDIA)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac Debug (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Linux Debug (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Mac Retina Debug", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_frame_rate_test": {
+                "builders": [
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_latency_tests": {
+                "builders": [
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_tests": {
+                "builders": [
+                    "Mac 10.8 Debug (Intel)", 
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Win7 Debug (NVIDIA)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac Debug (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Linux Debug (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Mac Retina Debug", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_throughput_tests": {
+                "builders": [
+                    "Mac Release (ATI)", 
+                    "Win7 Release (Intel)", 
+                    "Linux Release (ATI)", 
+                    "Mac Release (Intel)", 
+                    "Mac 10.8 Release (ATI)", 
+                    "Mac Retina Release", 
+                    "Mac 10.8 Release (Intel)", 
+                    "Win7 Release (NVIDIA)", 
+                    "Win7 Release (ATI)", 
+                    "Linux Release (NVIDIA)"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.gpu"
+    }, 
+    {
+        "name": "ChromiumGPUFYI", 
+        "tests": {
+            "content_unittests": {
+                "builders": [
+                    "Win7 Audio", 
+                    "Linux Audio"
+                ]
+            }, 
+            "gl_tests": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Debug (NVIDIA)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "gles2_conform_test": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Debug (NVIDIA)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_frame_rate_test": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_latency_tests": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_tests": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Debug (NVIDIA)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "gpu_throughput_tests": {
+                "builders": [
+                    "Linux Release (Intel)", 
+                    "WinXP Release (NVIDIA)"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Win7 Audio", 
+                    "Linux Audio"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.gpu.fyi"
+    }, 
+    {
+        "name": "ChromiumPerfAv", 
+        "tests": {
+            "layout-tests": {
+                "builders": [
+                    "AV Win7", 
+                    "AV Linux"
+                ]
+            }, 
+            "media_tests_av_perf": {
+                "builders": [
+                    "AV Win7", 
+                    "AV Linux"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.perf_av"
+    }, 
+    {
+        "name": "ChromiumWebkit", 
+        "tests": {
+            "Run tests": {
+                "builders": [
+                    "WebKit Android (GalaxyNexus)", 
+                    "WebKit (Content Shell) Android", 
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "android_webview_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "androidwebview_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "aura_unittests": {
+                "builders": [
+                    "Linux Aura"
+                ]
+            }, 
+            "base_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "cc_unittests": {
+                "builders": [
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "chrome_frame_net_tests": {
+                "builders": [
+                    "Chrome Frame Tests"
+                ]
+            }, 
+            "chrome_frame_tests": {
+                "builders": [
+                    "Chrome Frame Tests"
+                ]
+            }, 
+            "chrome_frame_unittests": {
+                "builders": [
+                    "Chrome Frame Tests"
+                ]
+            }, 
+            "chromiumtestshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "Win7 (dbg)", 
+                    "GPU Linux (NVIDIA)", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "GPU Mac10.7", 
+                    "GPU Win7 (dbg) (NVIDIA)", 
+                    "GPU Mac10.7 (dbg)", 
+                    "GPU Linux (dbg) (NVIDIA)", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "contentshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "gl_tests": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "GPU Linux (NVIDIA)", 
+                    "GPU Mac10.7", 
+                    "GPU Win7 (dbg) (NVIDIA)", 
+                    "GPU Mac10.7 (dbg)", 
+                    "GPU Linux (dbg) (NVIDIA)"
+                ]
+            }, 
+            "gpu_frame_rate_test": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "GPU Linux (NVIDIA)", 
+                    "GPU Mac10.7"
+                ]
+            }, 
+            "gpu_latency_tests": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "GPU Linux (NVIDIA)", 
+                    "GPU Mac10.7"
+                ]
+            }, 
+            "gpu_tests": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "GPU Linux (NVIDIA)", 
+                    "GPU Mac10.7", 
+                    "GPU Win7 (dbg) (NVIDIA)", 
+                    "GPU Mac10.7 (dbg)", 
+                    "GPU Linux (dbg) (NVIDIA)"
+                ]
+            }, 
+            "gpu_throughput_tests": {
+                "builders": [
+                    "GPU Win7 (NVIDIA)", 
+                    "GPU Linux (NVIDIA)", 
+                    "GPU Mac10.7"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "installer_util_unittests": {
+                "builders": [
+                    "Vista Tests"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "layout-tests": {
+                "builders": [
+                    "WebKit Linux 32", 
+                    "WebKit Win7", 
+                    "WebKit Linux ASAN", 
+                    "WebKit Win7 (dbg)(2)", 
+                    "WebKit (Content Shell) Linux", 
+                    "WebKit (Content Shell) Win", 
+                    "WebKit Android (GalaxyNexus)", 
+                    "WebKit (Content Shell) Android", 
+                    "WebKit Mac10.6 (dbg)", 
+                    "WebKit Mac10.7 (dbg)", 
+                    "WebKit Linux", 
+                    "WebKit Linux (dbg)", 
+                    "WebKit Mac10.6", 
+                    "WebKit Mac10.7", 
+                    "WebKit Linux (deps)", 
+                    "WebKit Mac10.8", 
+                    "WebKit Mac10.6 (deps)", 
+                    "WebKit (Content Shell) Mac10.6", 
+                    "WebKit Win7 (dbg)(1)", 
+                    "WebKit XP (deps)", 
+                    "WebKit XP"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "memory test: test_shell": {
+                "builders": [
+                    "Linux Valgrind"
+                ]
+            }, 
+            "memory_test": {
+                "builders": [
+                    "Mac10.6 Perf"
+                ]
+            }, 
+            "mini_installer_test": {
+                "builders": [
+                    "Vista Tests"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "new_tab_ui_cold_test": {
+                "builders": [
+                    "Win7 Perf", 
+                    "Mac10.6 Perf", 
+                    "Linux Perf"
+                ]
+            }, 
+            "new_tab_ui_warm_test": {
+                "builders": [
+                    "Win7 Perf", 
+                    "Mac10.6 Perf", 
+                    "Linux Perf"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "startup_test": {
+                "builders": [
+                    "Win7 Perf", 
+                    "Mac10.6 Perf", 
+                    "Linux Perf"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "tab_switching_test": {
+                "builders": [
+                    "Mac10.6 Perf"
+                ]
+            }, 
+            "test_report": {
+                "builders": [
+                    "WebKit Android (GalaxyNexus)", 
+                    "WebKit (Content Shell) Android", 
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "test_shell_tests": {
+                "builders": [
+                    "WebKit Linux 32", 
+                    "WebKit Win7", 
+                    "WebKit Mac10.6 (dbg)", 
+                    "WebKit Mac10.7 (dbg)", 
+                    "WebKit Linux", 
+                    "WebKit Linux (dbg)", 
+                    "WebKit Mac10.6", 
+                    "WebKit Mac10.7", 
+                    "WebKit Linux (deps)", 
+                    "WebKit Mac10.8", 
+                    "WebKit Mac10.6 (deps)", 
+                    "WebKit Win7 (dbg)(1)", 
+                    "WebKit XP (deps)", 
+                    "WebKit XP"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests", 
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)", 
+                    "Linux Aura", 
+                    "Mac10.8 Tests"
+                ]
+            }, 
+            "views_unittests": {
+                "builders": [
+                    "Win7 (dbg)", 
+                    "Vista Tests"
+                ]
+            }, 
+            "webkit_compositor_bindings_unittests": {
+                "builders": [
+                    "Linux Tests", 
+                    "Mac10.6 Tests", 
+                    "Android Tests (dbg)"
+                ]
+            }, 
+            "webkit_unit_tests": {
+                "builders": [
+                    "WebKit Linux 32", 
+                    "WebKit Win7", 
+                    "WebKit Android (GalaxyNexus)", 
+                    "WebKit Mac10.6 (dbg)", 
+                    "Android Tests (dbg)", 
+                    "WebKit Mac10.7 (dbg)", 
+                    "WebKit Linux", 
+                    "WebKit Linux (dbg)", 
+                    "WebKit Mac10.6", 
+                    "WebKit Mac10.7", 
+                    "WebKit Linux (deps)", 
+                    "WebKit Mac10.8", 
+                    "WebKit Mac10.6 (deps)", 
+                    "WebKit Win7 (dbg)(1)", 
+                    "WebKit XP (deps)", 
+                    "WebKit XP"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.webkit"
+    }, 
+    {
+        "name": "ChromiumFYI", 
+        "tests": {
+            "Run tests": {
+                "builders": [
+                    "Android ChromeDriver Tests (dbg)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "android_webview_unittests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "androidwebview_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "base_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "base_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "browser_tests": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Vista Tests (dbg)(2)", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (2)", 
+                    "Chromium Win x64 Clobber", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "browser_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "buildrunner_tests": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "cacheinvalidation_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "cacheinvalidation_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "cc_unittests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "chrome_frame_net_tests": {
+                "builders": [
+                    "Chrome Frame Tests (ie6)(peruser)", 
+                    "Chrome Frame Tests (ie8)(peruser)", 
+                    "Chrome Frame Tests (ie8)(dbg)", 
+                    "Chrome Frame Tests (ie7)(peruser)"
+                ]
+            }, 
+            "chrome_frame_tests": {
+                "builders": [
+                    "Chrome Frame Tests (ie6)(peruser)", 
+                    "Chrome Frame Tests (ie8)(peruser)", 
+                    "Chrome Frame Tests (ie8)(dbg)", 
+                    "Chrome Frame Tests (ie7)(peruser)"
+                ]
+            }, 
+            "chrome_frame_unittests": {
+                "builders": [
+                    "Chrome Frame Tests (ie6)(peruser)", 
+                    "Chrome Frame Tests (ie8)(peruser)", 
+                    "Chrome Frame Tests (ie8)(dbg)", 
+                    "Chrome Frame Tests (ie7)(peruser)"
+                ]
+            }, 
+            "chromedriver2_python_tests": {
+                "builders": [
+                    "Android ChromeDriver Tests (dbg)"
+                ]
+            }, 
+            "chromedriver2_unittests": {
+                "builders": [
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win x64 Clobber"
+                ]
+            }, 
+            "chromiumtestshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "components_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "content_browsertests": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Vista Tests (dbg)(2)", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (2)", 
+                    "Chromium Win x64 Clobber", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "content_browsertests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "content_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "content_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "contentshell_instrumentation_tests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "courgette_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "courgette_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "crypto_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "crypto_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "dbus_unittests": {
+                "builders": [
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "dbus_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "device_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "googleurl_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "googleurl_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "gpu_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "gpu_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "installer_util_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "interactive_ui_tests": {
+                "builders": [
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win x64 Clobber"
+                ]
+            }, 
+            "ipc_tests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "ipc_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "jingle_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Win ASAN Tests (1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "jingle_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "media_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "media_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "memory test: base_unittests": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: browser_tests": {
+                "builders": [
+                    "Windows Browser (DrMemory) (1)", 
+                    "Windows Browser (DrMemory) (6)", 
+                    "Chromium Linux Browser (valgrind) (1)", 
+                    "Windows Browser (DrMemory) (5)", 
+                    "Chromium Linux Browser (valgrind) (2)", 
+                    "Windows Browser (DrMemory) (4)", 
+                    "Chromium Linux Browser (valgrind) (3)", 
+                    "Windows Browser (DrMemory) (3)", 
+                    "Chromium Linux Browser (valgrind) (4)", 
+                    "Windows Browser (DrMemory) (2)"
+                ]
+            }, 
+            "memory test: browser_tests_1": {
+                "builders": [
+                    "Chromium Linux Browser (valgrind) (1)", 
+                    "Chromium Linux Browser (valgrind) (2)", 
+                    "Chromium Linux Browser (valgrind) (3)", 
+                    "Chromium Linux Browser (valgrind) (4)"
+                ]
+            }, 
+            "memory test: content": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)"
+                ]
+            }, 
+            "memory test: content_browsertests": {
+                "builders": [
+                    "Chromium Linux Browser (valgrind) (1)", 
+                    "Chromium Linux Browser (valgrind) (2)", 
+                    "Chromium Linux Browser (valgrind) (3)", 
+                    "Chromium Linux Browser (valgrind) (4)"
+                ]
+            }, 
+            "memory test: crypto": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: device_unittests": {
+                "builders": [
+                    "Linux Tests (tsan RV)"
+                ]
+            }, 
+            "memory test: googleurl": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: interactive_ui_tests": {
+                "builders": [
+                    "Chromium Linux Reliability (valgrind)"
+                ]
+            }, 
+            "memory test: interactive_ui_tests_1": {
+                "builders": [
+                    "Chromium Linux Reliability (valgrind)"
+                ]
+            }, 
+            "memory test: ipc_tests": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: media": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: net": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: printing": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: remoting": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: sql": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Windows Tests (TSan Win 7)", 
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: sync": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Linux Tests (tsan RV)"
+                ]
+            }, 
+            "memory test: ui_unittests": {
+                "builders": [
+                    "Chromium Mac 10.6 (tsan)", 
+                    "Linux Tests (tsan RV)"
+                ]
+            }, 
+            "memory test: unit": {
+                "builders": [
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)", 
+                    "Windows Tests (DrMemory pattern)"
+                ]
+            }, 
+            "memory test: unit_1": {
+                "builders": [
+                    "Linux Tests (tsan RV)", 
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)"
+                ]
+            }, 
+            "memory test: unit_2": {
+                "builders": [
+                    "Windows Tests (DrMemory XP)", 
+                    "Windows Tests (DrMemory full)"
+                ]
+            }, 
+            "memory test: unit_3": {
+                "builders": [
+                    "Windows Tests (DrMemory full)"
+                ]
+            }, 
+            "memory test: unit_4": {
+                "builders": [
+                    "Windows Tests (DrMemory full)"
+                ]
+            }, 
+            "memory test: unit_5": {
+                "builders": [
+                    "Windows Tests (DrMemory full)"
+                ]
+            }, 
+            "memory_test": {
+                "builders": [
+                    "Chromium Linux Memory", 
+                    "Chromium Vista Memory", 
+                    "Chromium Mac Memory"
+                ]
+            }, 
+            "mini_installer_test": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)"
+                ]
+            }, 
+            "mini_installer_test_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "net_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "net_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "new_tab_ui_cold_test": {
+                "builders": [
+                    "Chromium Linux Perf Annotator"
+                ]
+            }, 
+            "new_tab_ui_warm_test": {
+                "builders": [
+                    "Chromium Linux Perf Annotator"
+                ]
+            }, 
+            "ppapi_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "ppapi_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "printing_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "printing_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "remoting_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Linux TSan v2", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium Mac 10.8 Buildrunner"
+                ]
+            }, 
+            "remoting_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Mac Buildrunner", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Mac 10.8 Buildrunner", 
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "run_browser_tests_only": {
+                "builders": [
+                    "Linux Coverage (dbg)"
+                ]
+            }, 
+            "run_unittests_only": {
+                "builders": [
+                    "Linux Coverage (dbg)"
+                ]
+            }, 
+            "sandbox_linux_jni_unittests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)"
+                ]
+            }, 
+            "sandbox_linux_unittests": {
+                "builders": [
+                    "Chromium Linux TSan v2", 
+                    "Linux ARM Tests (Panda)"
+                ]
+            }, 
+            "sbox_integration_tests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "sbox_integration_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "sbox_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "sbox_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "sbox_validation_tests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "sbox_validation_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Windows Buildrunner"
+                ]
+            }, 
+            "sql_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "sql_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "startup_test": {
+                "builders": [
+                    "Chromium Linux Perf Annotator"
+                ]
+            }, 
+            "sync_integration_tests": {
+                "builders": [
+                    "Chromium Win x64 Clobber"
+                ]
+            }, 
+            "sync_unit_tests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "sync_unit_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "telemetry_perf_unittests": {
+                "builders": [
+                    "Chromium Mac Perf Annotator", 
+                    "Chromium Windows Perf Annotator", 
+                    "Chromium Linux Perf Annotator"
+                ]
+            }, 
+            "telemetry_unittests": {
+                "builders": [
+                    "Chromium Mac Perf Annotator", 
+                    "Chromium Windows Perf Annotator", 
+                    "Chromium Linux Perf Annotator"
+                ]
+            }, 
+            "test_report": {
+                "builders": [
+                    "Android ChromeDriver Tests (dbg)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "ui_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Win ASAN Tests (1)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "ui_unittests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "unit_tests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Linux TSan v2", 
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Win ASAN Tests (2)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Linux Redux", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Linux Buildrunner", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)", 
+                    "Chromium iOS Simulator (dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "unit_tests_buildrunner_ignore": {
+                "builders": [
+                    "Chromium Linux Buildrunner"
+                ]
+            }, 
+            "views_unittests": {
+                "builders": [
+                    "Chromium Win7 Ninja Goma (shared)", 
+                    "Chromium Win VS2012 Goma", 
+                    "Chromium Win VS2012", 
+                    "Chromium Win (aura)", 
+                    "Chromium Win7 Ninja Goma", 
+                    "Chromium Win Ninja Goma (shared)", 
+                    "Chromium Win Ninja Goma", 
+                    "Chromium Win VS2012 Goma (dbg)", 
+                    "Chromium Win (ash)", 
+                    "Win8 Tests (1)", 
+                    "Chromium Win VS2012 (dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Vista Tests (dbg)(1)"
+                ]
+            }, 
+            "webkit_compositor_bindings_unittests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Linux ARM Tests (Panda)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Chromium Win x64 Clobber", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }, 
+            "webkit_unit_tests": {
+                "builders": [
+                    "Android Tests (JB Nexus7)(dbg)", 
+                    "Android Tests (ICS GalaxyNexus)(dbg)", 
+                    "Android Tests (JB GalaxyNexus)(dbg)", 
+                    "Android Asan Tests (dbg)"
+                ]
+            }
+        }, 
+        "url": "http://build.chromium.org/p/chromium.fyi"
+    }, 
+    {
+        "name": "webkit.org", 
+        "tests": {
+            "layout-tests": {
+                "builders": [
+                    "Apple Lion (Leaks)", 
+                    "Apple Lion Debug WK1 (Tests)", 
+                    "Apple Lion Debug WK2 (Tests)", 
+                    "Apple Lion Release WK1 (Tests)", 
+                    "Apple Lion Release WK2 (Tests)", 
+                    "Apple MountainLion (Leaks)", 
+                    "Apple MountainLion Debug WK1 (Tests)", 
+                    "Apple MountainLion Debug WK2 (Tests)", 
+                    "Apple MountainLion Release WK1 (Tests)", 
+                    "Apple MountainLion Release WK2 (Tests)", 
+                    "Apple Win 7 Debug (Tests)", 
+                    "Apple Win 7 Release (Tests)", 
+                    "Chromium Android Release (Tests)", 
+                    "Chromium Linux Release (Tests)", 
+                    "Chromium Mac Release (Tests)", 
+                    "Chromium Win Release (Tests)", 
+                    "EFL Linux 64-bit Debug WK2", 
+                    "EFL Linux 64-bit Release", 
+                    "EFL Linux 64-bit Release WK2", 
+                    "GTK Linux 32-bit Release", 
+                    "GTK Linux 64-bit Debug WK1", 
+                    "GTK Linux 64-bit Release", 
+                    "GTK Linux 64-bit Release WK2 (Tests)", 
+                    "Qt Linux Release", 
+                    "WinCairo Release"
+                ]
+            }
+        }, 
+        "url": "http://build.webkit.org"
+    }
+]);
diff --git a/Tools/TestResultServer/static-dashboards/builders_unittests.js b/Tools/TestResultServer/static-dashboards/builders_unittests.js
new file mode 100644
index 0000000..29c02f1
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/builders_unittests.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2012 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.
+
+test('loading steps', 4, function() {
+    var tests = {}
+    var baseUrl = 'http://dummyurl';
+    var name = 'dummyname';
+    var master = new builders.BuilderMaster(name, baseUrl, tests);
+
+    var builder = 'dummybuilder';
+    var buildNumber = 12345;
+    equal(master.logPath(builder, buildNumber), baseUrl + '/builders/' + builder + '/builds/' + buildNumber);
+    equal(master.builderJsonPath(), baseUrl + '/json/builders');
+    equal(master.tests, tests);
+    equal(master.name, name);
+})
diff --git a/Tools/TestResultServer/static-dashboards/dashboard_base.js b/Tools/TestResultServer/static-dashboards/dashboard_base.js
index 6ec0c8e..8b1bcbf 100644
--- a/Tools/TestResultServer/static-dashboards/dashboard_base.js
+++ b/Tools/TestResultServer/static-dashboards/dashboard_base.js
@@ -32,17 +32,15 @@
 //
 // The calling page is expected to implement the following "abstract"
 // functions/objects:
-var g_pageLoadStartTime = Date.now();
-var g_resourceLoader;
 
 // Generates the contents of the dashboard. The page should override this with
 // a function that generates the page assuming all resources have loaded.
 function generatePage() {}
 
-// Takes a key and a value and sets the g_currentState[key] = value iff key is
+// Takes a key and a value and sets the g_history.dashboardSpecificState[key] = value iff key is
 // a valid hash parameter and the value is a valid value for that key.
 //
-// @return {boolean} Whether the key what inserted into the g_currentState.
+// @return {boolean} Whether the key what inserted into the g_history.dashboardSpecificState.
 function handleValidHashParameter(key, value)
 {
     return false;
@@ -86,13 +84,11 @@
     'O': 'MISSING'
 };
 
-var FAILURE_EXPECTATIONS_ = {
-    'T': 1,
-    'F': 1,
-    'C': 1,
-    'I': 1,
-    'Z': 1
+// Map of parameter to other parameter it invalidates.
+var CROSS_DB_INVALIDATING_PARAMETERS = {
+    'testType': 'group'
 };
+var DB_SPECIFIC_INVALIDATING_PARAMETERS;
 
 // Keys in the JSON files.
 var WONTFIX_COUNTS_KEY = 'wontfixCounts';
@@ -146,9 +142,12 @@
     'unit_tests',
     'views_unittests',
     'webkit_unit_tests',
+    'androidwebview_instrumentation_tests',
+    'chromiumtestshell_instrumentation_tests',
+    'contentshell_instrumentation_tests',
+    'cc_unittests'
 ];
 
-var RELOAD_REQUIRING_PARAMETERS = ['showAllRuns', 'group', 'testType'];
 
 // Enum for indexing into the run-length encoded results in the JSON files.
 // 0 is where the count is length is stored. 1 is the value.
@@ -162,251 +161,34 @@
     return 'FSTOCIZ'.indexOf(value) != -1;
 }
 
-// Takes a key and a value and sets the g_currentState[key] = value iff key is
-// a valid hash parameter and the value is a valid value for that key. Handles
-// cross-dashboard parameters then falls back to calling
-// handleValidHashParameter for dashboard-specific parameters.
-//
-// @return {boolean} Whether the key what inserted into the g_currentState.
-function handleValidHashParameterWrapper(key, value)
-{
-    switch(key) {
-    case 'testType':
-        validateParameter(g_crossDashboardState, key, value,
-            function() { return TEST_TYPES.indexOf(value) != -1; });
-        return true;
-
-    case 'group':
-        validateParameter(g_crossDashboardState, key, value,
-            function() {
-              return value in LAYOUT_TESTS_BUILDER_GROUPS ||
-                  value in CHROMIUM_GPU_TESTS_BUILDER_GROUPS ||
-                  value in CHROMIUM_GTESTS_BUILDER_GROUPS;
-            });
-        return true;
-
-    // FIXME: This should probably be stored on g_crossDashboardState like everything else in this function.
-    case 'builder':
-        validateParameter(g_currentState, key, value,
-            function() { return value in g_builders; });
-        return true;
-
-    case 'useTestData':
-    case 'showAllRuns':
-        g_crossDashboardState[key] = value == 'true';
-        return true;
-
-    case 'buildDir':
-        if (value === 'Debug' || value == 'Release') {
-            g_crossDashboardState['testType'] = 'layout-tests';
-            g_crossDashboardState[key] = value;
-            return true;
-        } else
-            return false;
-
-    default:
-        return handleValidHashParameter(key, value);
-    }
-}
-
-var g_defaultCrossDashboardStateValues = {
-    group: '@ToT - chromium.org',
-    showAllRuns: false,
-    testType: 'layout-tests',
-    buildDir: '',
-    useTestData: false,
-}
-
 // Generic utility functions.
 function $(id)
 {
     return document.getElementById(id);
 }
 
-function stringContains(a, b)
-{
-    return a.indexOf(b) != -1;
-}
-
-function caseInsensitiveContains(a, b)
-{
-    return a.match(new RegExp(b, 'i'));
-}
-
-function startsWith(a, b)
-{
-    return a.indexOf(b) == 0;
-}
-
-function endsWith(a, b)
-{
-    return a.lastIndexOf(b) == a.length - b.length;
-}
-
-function isValidName(str)
-{
-    return str.match(/[A-Za-z0-9\-\_,]/);
-}
-
-function trimString(str)
-{
-    return str.replace(/^\s+|\s+$/g, '');
-}
-
-function collapseWhitespace(str)
-{
-    return str.replace(/\s+/g, ' ');
-}
-
-function validateParameter(state, key, value, validateFn)
-{
-    if (validateFn())
-        state[key] = value;
-    else
-        console.log(key + ' value is not valid: ' + value);
-}
-
-function queryHashAsMap()
-{
-    var hash = window.location.hash;
-    var paramsList = hash ? hash.substring(1).split('&') : [];
-    var paramsMap = {};
-    var invalidKeys = [];
-    for (var i = 0; i < paramsList.length; i++) {
-        var thisParam = paramsList[i].split('=');
-        if (thisParam.length != 2) {
-            console.log('Invalid query parameter: ' + paramsList[i]);
-            continue;
-        }
-
-        paramsMap[thisParam[0]] = decodeURIComponent(thisParam[1]);
-    }
-
-    // FIXME: remove support for mapping from the master parameter to the group
-    // one once the waterfall starts to pass in the builder name instead.
-    if (paramsMap.master) {
-        paramsMap.group = LEGACY_BUILDER_MASTERS_TO_GROUPS[paramsMap.master];
-        if (!paramsMap.group)
-            console.log('ERROR: Unknown master name: ' + paramsMap.master);
-        window.location.hash = window.location.hash.replace('master=' + paramsMap.master, 'group=' + paramsMap.group);
-        delete paramsMap.master;
-    }
-
-    return paramsMap;
-}
-
-function parseParameter(parameters, key)
-{
-    if (!(key in parameters))
-        return;
-    var value = parameters[key];
-    if (!handleValidHashParameterWrapper(key, value))
-        console.log("Invalid query parameter: " + key + '=' + value);
-}
-
-function parseCrossDashboardParameters()
-{
-    g_crossDashboardState = {};
-    var parameters = queryHashAsMap();
-    for (parameterName in g_defaultCrossDashboardStateValues)
-        parseParameter(parameters, parameterName);
-
-    fillMissingValues(g_crossDashboardState, g_defaultCrossDashboardStateValues);
-    if (currentBuilderGroup() === undefined)
-        g_crossDashboardState.group = g_defaultCrossDashboardStateValues.group;
-}
-
 function parseDashboardSpecificParameters()
 {
-    g_currentState = {};
-    var parameters = queryHashAsMap();
+    g_history.dashboardSpecificState = {};
+    var parameters = history.queryHashAsMap();
     for (parameterName in g_defaultDashboardSpecificStateValues)
-        parseParameter(parameters, parameterName);
-}
-
-function parseParameters()
-{
-    var oldCrossDashboardState = g_crossDashboardState;
-    var oldDashboardSpecificState = g_currentState;
-
-    parseCrossDashboardParameters();
-    parseDashboardSpecificParameters();
-    parseParameter(queryHashAsMap(), 'builder');
-
-    var crossDashboardDiffState = diffStates(oldCrossDashboardState, g_crossDashboardState);
-    var dashboardSpecificDiffState = diffStates(oldDashboardSpecificState, g_currentState);
-
-    fillMissingValues(g_currentState, g_defaultDashboardSpecificStateValues);
-    if (!g_crossDashboardState.useTestData)
-        fillMissingValues(g_currentState, {'builder': g_defaultBuilderName});
-
-    // FIXME: dashboard_base shouldn't know anything about specific dashboard specific keys.
-    if (dashboardSpecificDiffState.builder)
-        delete g_currentState.tests;
-    if (g_currentState.tests)
-        delete g_currentState.builder;
-
-    // Some parameters require loading different JSON files when the value changes. Do a reload.
-    if (Object.keys(oldCrossDashboardState).length) {
-        for (var key in g_crossDashboardState) {
-            if (oldCrossDashboardState[key] != g_crossDashboardState[key] && RELOAD_REQUIRING_PARAMETERS.indexOf(key) != -1)
-                window.location.reload();
-        }
-    }
-
-    return dashboardSpecificDiffState;
-}
-
-function diffStates(oldState, newState)
-{
-    // If there is no old state, everything in the current state is new.
-    if (!oldState)
-        return newState;
-
-    var changedParams = {};
-    for (curKey in newState) {
-        var oldVal = oldState[curKey];
-        var newVal = newState[curKey];
-        // Add new keys or changed values.
-        if (!oldVal || oldVal != newVal)
-            changedParams[curKey] = newVal;
-    }
-    return changedParams;
+        g_history.parseParameter(parameters, parameterName);
 }
 
 function defaultValue(key)
 {
     if (key in g_defaultDashboardSpecificStateValues)
         return g_defaultDashboardSpecificStateValues[key];
-    return g_defaultCrossDashboardStateValues[key];
+    return history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES[key];
 }
 
-function fillMissingValues(to, from)
-{
-    for (var state in from) {
-        if (!(state in to))
-            to[state] = from[state];
-    }
-}
-
-// FIXME: Rename this to g_dashboardSpecificState;
-var g_currentState = {};
-var g_crossDashboardState = {};
-parseCrossDashboardParameters();
-
-function isLayoutTestResults()
-{
-    return g_crossDashboardState.testType == 'layout-tests';
-}
-
-function isGPUTestResults()
-{
-    return g_crossDashboardState.testType == 'gpu_tests';
-}
+// TODO(jparent): Each db should create their own history obj, not global.
+var g_history = new history.History();
+g_history.parseCrossDashboardParameters();
 
 function currentBuilderGroupCategory()
 {
-    switch (g_crossDashboardState.testType) {
+    switch (g_history.crossDashboardState.testType) {
     case 'gl_tests':
     case 'gpu_tests':
         return CHROMIUM_GPU_TESTS_BUILDER_GROUPS;
@@ -415,19 +197,30 @@
     case 'test_shell_tests':
     case 'webkit_unit_tests':
         return TEST_SHELL_TESTS_BUILDER_GROUPS;
+    case 'androidwebview_instrumentation_tests':
+    case 'chromiumtestshell_instrumentation_tests':
+    case 'contentshell_instrumentation_tests':
+        return CHROMIUM_INSTRUMENTATION_TESTS_BUILDER_GROUPS;
+    case 'cc_unittests':
+        return CC_UNITTEST_BUILDER_GROUPS;
     default:
         return CHROMIUM_GTESTS_BUILDER_GROUPS;
     }
 }
 
-function currentBuilderGroup()
+function currentBuilderGroupName()
 {
-    return currentBuilderGroupCategory()[g_crossDashboardState.group]
+    return g_history.crossDashboardState.group || Object.keys(currentBuilderGroupCategory())[0];
 }
 
-function builderMaster(builderName)
+function currentBuilderGroup()
 {
-    return BUILDER_TO_MASTER[builderName];
+    return currentBuilderGroupCategory()[currentBuilderGroupName()];
+}
+
+function currentBuilders()
+{
+    return currentBuilderGroup().builders;
 }
 
 function isTipOfTreeWebKitBuilder()
@@ -435,212 +228,24 @@
     return currentBuilderGroup().isToTWebKit;
 }
 
-var g_defaultBuilderName, g_builders;
-function initBuilders()
-{
-    if (g_crossDashboardState.buildDir) {
-        // If buildDir is set, point to the results.json in the local tree. Useful for debugging changes to the python JSON generator.
-        g_defaultBuilderName = 'DUMMY_BUILDER_NAME';
-        g_builders = {'DUMMY_BUILDER_NAME': ''};
-        var loc = document.location.toString();
-        var offset = loc.indexOf('webkit/');
-    } else
-        currentBuilderGroup().setup();
-}
-
 var g_resultsByBuilder = {};
 var g_expectationsByPlatform = {};
-var g_staleBuilders = [];
-var g_buildersThatFailedToLoad = [];
-
-// TODO(aboxhall): figure out whether this is a performance bottleneck and
-// change calling code to understand the trie structure instead if necessary.
-function flattenTrie(trie, prefix)
-{
-    var result = {};
-    for (var name in trie) {
-        var fullName = prefix ? prefix + "/" + name : name;
-        var data = trie[name];
-        if ("results" in data)
-            result[fullName] = data;
-        else {
-            var partialResult = flattenTrie(data, fullName);
-            for (var key in partialResult) {
-                result[key] = partialResult[key];
-            }
-        }
-    }
-    return result;
-}
-
-function isTreeMap()
-{
-    return endsWith(window.location.pathname, 'treemap.html');
-}
 
 function isFlakinessDashboard()
 {
-    return endsWith(window.location.pathname, 'flakiness_dashboard.html');
-}
-
-var g_hasDoneInitialPageGeneration = false;
-// String of error messages to display to the user.
-var g_errorMessages = '';
-
-// Record a new error message.
-// @param {string} errorMsg The message to show to the user.
-function addError(errorMsg)
-{
-    g_errorMessages += errorMsg + '<br>';
-}
-
-// Clear out error and warning messages.
-function clearErrors()
-{
-    g_errorMessages = '';
-}
-
-// If there are errors, show big and red UI for errors so as to be noticed.
-function showErrors()
-{
-    var errors = $('errors');
-
-    if (!g_errorMessages) {
-        if (errors)
-            errors.parentNode.removeChild(errors);
-        return;
-    }
-
-    if (!errors) {
-        errors = document.createElement('H2');
-        errors.style.color = 'red';
-        errors.id = 'errors';
-        document.body.appendChild(errors);
-    }
-
-    errors.innerHTML = g_errorMessages;
-}
-
-function addBuilderLoadErrors()
-{
-    if (g_hasDoneInitialPageGeneration)
-        return;
-
-    if (g_buildersThatFailedToLoad.length)
-        addError('ERROR: Failed to get data from ' + g_buildersThatFailedToLoad.toString() + '.');
-
-    if (g_staleBuilders.length)
-        addError('ERROR: Data from ' + g_staleBuilders.toString() + ' is more than 1 day stale.');
-}
-
-function resourceLoadingComplete()
-{
-    g_resourceLoader = null;
-    handleLocationChange();
+    return string.endsWith(window.location.pathname, 'flakiness_dashboard.html');
 }
 
 function handleLocationChange()
 {
-    if (g_resourceLoader)
-        return;
-
-    addBuilderLoadErrors();
-    g_hasDoneInitialPageGeneration = true;
-
-    var params = parseParameters();
-    var shouldGeneratePage = true;
-    if (Object.keys(params).length)
-        shouldGeneratePage = handleQueryParameterChange(params);
-
-    var newHash = permaLinkURLHash();
-    var winHash = window.location.hash || "#";
-    // Make sure the location is the same as the state we are using internally.
-    // These get out of sync if processQueryParamChange changed state.
-    if (newHash != winHash) {
-        // This will cause another hashchange, and when we loop
-        // back through here next time, we'll go through generatePage.
-        window.location.hash = newHash;
-    } else if (shouldGeneratePage)
+    if (g_history.parseParameters())
         generatePage();
 }
 
-window.onhashchange = handleLocationChange;
-
-function combinedDashboardState()
-{
-    var combinedState = Object.create(g_currentState);
-    for (var key in g_crossDashboardState)
-        combinedState[key] = g_crossDashboardState[key];
-    return combinedState;    
-}
-
-// Sets the page state. Takes varargs of key, value pairs.
-function setQueryParameter(var_args)
-{
-    var state = combinedDashboardState();
-    for (var i = 0; i < arguments.length; i += 2) {
-        var key = arguments[i];
-        state[key] = arguments[i + 1];
-    }
-    // Note: We use window.location.hash rather that window.location.replace
-    // because of bugs in Chrome where extra entries were getting created
-    // when back button was pressed and full page navigation was occuring.
-    // FIXME: file those bugs.
-    window.location.hash = permaLinkURLHash(state);
-}
-
-function permaLinkURLHash(opt_state)
-{
-    var state = opt_state || combinedDashboardState();
-    return '#' + joinParameters(state);
-}
-
-function joinParameters(stateObject)
-{
-    var state = [];
-    for (var key in stateObject) {
-        var value = stateObject[key];
-        if (value != defaultValue(key))
-            state.push(key + '=' + encodeURIComponent(value));
-    }
-    return state.join('&');
-}
-
-function logTime(msg, startTime)
-{
-    console.log(msg + ': ' + (Date.now() - startTime));
-}
-
-function hidePopup()
-{
-    var popup = $('popup');
-    if (popup)
-        popup.parentNode.removeChild(popup);
-}
-
-function showPopup(target, html)
-{
-    var popup = $('popup');
-    if (!popup) {
-        popup = document.createElement('div');
-        popup.id = 'popup';
-        document.body.appendChild(popup);
-    }
-
-    // Set html first so that we can get accurate size metrics on the popup.
-    popup.innerHTML = html;
-
-    var targetRect = target.getBoundingClientRect();
-
-    var x = Math.min(targetRect.left - 10, document.documentElement.clientWidth - popup.offsetWidth);
-    x = Math.max(0, x);
-    popup.style.left = x + document.body.scrollLeft + 'px';
-
-    var y = targetRect.top + targetRect.height;
-    if (y + popup.offsetHeight > document.documentElement.clientHeight)
-        y = targetRect.top - popup.offsetHeight;
-    y = Math.max(0, y);
-    popup.style.top = y + document.body.scrollTop + 'px';
+// TODO(jparent): Move this to upcoming History object.
+function intializeHistory() {
+    window.onhashchange = handleLocationChange;
+    handleLocationChange();
 }
 
 // Create a new function with some of its arguements
@@ -662,254 +267,8 @@
     };
 };
 
-// Returns the appropriate expectatiosn map for the current testType.
+// Returns the appropriate expectations map for the current testType.
 function expectationsMap()
 {
-    return isLayoutTestResults() ? LAYOUT_TEST_EXPECTATIONS_MAP_ : GTEST_EXPECTATIONS_MAP_;
-}
-
-function toggleQueryParameter(param)
-{
-    setQueryParameter(param, !queryParameterValue(param));
-}
-
-function queryParameterValue(parameter)
-{
-    return g_currentState[parameter] || g_crossDashboardState[parameter];
-}
-
-function checkboxHTML(queryParameter, label, isChecked, opt_extraJavaScript)
-{
-    var js = opt_extraJavaScript || '';
-    return '<label style="padding-left: 2em">' +
-        '<input type="checkbox" onchange="toggleQueryParameter(\'' + queryParameter + '\');' + js + '" ' +
-            (isChecked ? 'checked' : '') + '>' + label +
-        '</label> ';
-}
-
-function selectHTML(label, queryParameter, options)
-{
-    var html = '<label style="padding-left: 2em">' + label + ': ' +
-        '<select onchange="setQueryParameter(\'' + queryParameter + '\', this[this.selectedIndex].value)">';
-
-    for (var i = 0; i < options.length; i++) {
-        var value = options[i];
-        html += '<option value="' + value + '" ' +
-            (queryParameterValue(queryParameter) == value ? 'selected' : '') +
-            '>' + value + '</option>'
-    }
-    html += '</select></label> ';
-    return html;
-}
-
-// Returns the HTML for the select element to switch to different testTypes.
-function htmlForTestTypeSwitcher(opt_noBuilderMenu, opt_extraHtml, opt_includeNoneBuilder)
-{
-    var html = '<div style="border-bottom:1px dashed">';
-    html += '' +
-        htmlForDashboardLink('Stats', 'aggregate_results.html') +
-        htmlForDashboardLink('Timeline', 'timeline_explorer.html') +
-        htmlForDashboardLink('Results', 'flakiness_dashboard.html') +
-        htmlForDashboardLink('Treemap', 'treemap.html');
-
-    html += selectHTML('Test type', 'testType', TEST_TYPES);
-
-    if (!opt_noBuilderMenu) {
-        var buildersForMenu = Object.keys(g_builders);
-        if (opt_includeNoneBuilder)
-            buildersForMenu.unshift('--------------');
-        html += selectHTML('Builder', 'builder', buildersForMenu);
-    }
-
-    html += selectHTML('Group', 'group',
-        Object.keys(currentBuilderGroupCategory()));
-
-    if (!isTreeMap())
-        html += checkboxHTML('showAllRuns', 'Show all runs', g_crossDashboardState.showAllRuns);
-
-    if (opt_extraHtml)
-        html += opt_extraHtml;
-    return html + '</div>';
-}
-
-function selectBuilder(builder)
-{
-    setQueryParameter('builder', builder);
-}
-
-function loadDashboard(fileName)
-{
-    var pathName = window.location.pathname;
-    pathName = pathName.substring(0, pathName.lastIndexOf('/') + 1);
-    window.location = pathName + fileName + window.location.hash;
-}
-
-function htmlForTopLink(html, onClick, isSelected)
-{
-    var cssText = isSelected ? 'font-weight: bold;' : 'color:blue;text-decoration:underline;cursor:pointer;';
-    cssText += 'margin: 0 5px;';
-    return '<span style="' + cssText + '" onclick="' + onClick + '">' + html + '</span>';
-}
-
-function htmlForDashboardLink(html, fileName)
-{
-    var pathName = window.location.pathname;
-    var currentFileName = pathName.substring(pathName.lastIndexOf('/') + 1);
-    var isSelected = currentFileName == fileName;
-    var onClick = 'loadDashboard(\'' + fileName + '\')';
-    return htmlForTopLink(html, onClick, isSelected);
-}
-
-function revisionLink(results, index, key, singleUrlTemplate, rangeUrlTemplate)
-{
-    var currentRevision = parseInt(results[key][index], 10);
-    var previousRevision = parseInt(results[key][index + 1], 10);
-
-    function singleUrl()
-    {
-        return singleUrlTemplate.replace('<rev>', currentRevision);
-    }
-
-    function rangeUrl()
-    {
-        return rangeUrlTemplate.replace('<rev1>', currentRevision).replace('<rev2>', previousRevision + 1);
-    }
-
-    if (currentRevision == previousRevision)
-        return 'At <a href="' + singleUrl() + '">r' + currentRevision    + '</a>';
-    else if (currentRevision - previousRevision == 1)
-        return '<a href="' + singleUrl() + '">r' + currentRevision    + '</a>';
-    else
-        return '<a href="' + rangeUrl() + '">r' + (previousRevision + 1) + ' to r' + currentRevision + '</a>';
-}
-
-function chromiumRevisionLink(results, index)
-{
-    return revisionLink(
-        results,
-        index,
-        CHROME_REVISIONS_KEY,
-        'http://src.chromium.org/viewvc/chrome?view=rev&revision=<rev>',
-        'http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&range=<rev2>:<rev1>&mode=html');
-}
-
-function webKitRevisionLink(results, index)
-{
-    return revisionLink(
-        results,
-        index,
-        WEBKIT_REVISIONS_KEY,
-        'http://trac.webkit.org/changeset/<rev>',
-        'http://trac.webkit.org/log/trunk/?rev=<rev1>&stop_rev=<rev2>&limit=100&verbose=on');
-}
-
-// "Decompresses" the RLE-encoding of test results so that we can query it
-// by build index and test name.
-//
-// @param {Object} results results for the current builder
-// @return Object with these properties:
-//     - testNames: array mapping test index to test names.
-//     - resultsByBuild: array of builds, for each build a (sparse) array of test results by test index.
-//     - flakyTests: array with the boolean value true at test indices that are considered flaky (more than one single-build failure).
-//     - flakyDeltasByBuild: array of builds, for each build a count of flaky test results by expectation, as well as a total.
-function decompressResults(builderResults)
-{
-    var builderTestResults = builderResults[TESTS_KEY];
-    var buildCount = builderResults[FIXABLE_COUNTS_KEY].length;
-    var resultsByBuild = new Array(buildCount);
-    var flakyDeltasByBuild = new Array(buildCount);
-
-    // Pre-sizing the test result arrays for each build saves us ~250ms
-    var testCount = 0;
-    for (var testName in builderTestResults)
-        testCount++;
-    for (var i = 0; i < buildCount; i++) {
-        resultsByBuild[i] = new Array(testCount);
-        resultsByBuild[i][testCount - 1] = undefined;
-        flakyDeltasByBuild[i] = {};
-    }
-
-    // Using indices instead of the full test names for each build saves us
-    // ~1500ms
-    var testIndex = 0;
-    var testNames = new Array(testCount);
-    var flakyTests = new Array(testCount);
-
-    // Decompress and "invert" test results (by build instead of by test) and
-    // determine which are flaky.
-    for (var testName in builderTestResults) {
-        var oneBuildFailureCount = 0;
-
-        testNames[testIndex] = testName;
-        var testResults = builderTestResults[testName].results;
-        for (var i = 0, rleResult, currentBuildIndex = 0; (rleResult = testResults[i]) && currentBuildIndex < buildCount; i++) {
-            var count = rleResult[RLE.LENGTH];
-            var value = rleResult[RLE.VALUE];
-
-            if (count == 1 && value in FAILURE_EXPECTATIONS_)
-                oneBuildFailureCount++;
-
-            for (var j = 0; j < count; j++) {
-                resultsByBuild[currentBuildIndex++][testIndex] = value;
-                if (currentBuildIndex == buildCount)
-                    break;
-            }
-        }
-
-        if (oneBuildFailureCount > 2)
-            flakyTests[testIndex] = true;
-
-        testIndex++;
-    }
-
-    // Now that we know which tests are flaky, count the test results that are
-    // from flaky tests for each build.
-    testIndex = 0;
-    for (var testName in builderTestResults) {
-        if (!flakyTests[testIndex++])
-            continue;
-
-        var testResults = builderTestResults[testName].results;
-        for (var i = 0, rleResult, currentBuildIndex = 0; (rleResult = testResults[i]) && currentBuildIndex < buildCount; i++) {
-            var count = rleResult[RLE.LENGTH];
-            var value = rleResult[RLE.VALUE];
-
-            for (var j = 0; j < count; j++) {
-                var buildTestResults = flakyDeltasByBuild[currentBuildIndex++];
-                function addFlakyDelta(key)
-                {
-                    if (!(key in buildTestResults))
-                        buildTestResults[key] = 0;
-                    buildTestResults[key]++;
-                }
-                addFlakyDelta(value);
-                if (value != 'P' && value != 'N')
-                    addFlakyDelta('total');
-                if (currentBuildIndex == buildCount)
-                    break;
-            }
-        }
-    }
-
-    return {
-        testNames: testNames,
-        resultsByBuild: resultsByBuild,
-        flakyTests: flakyTests,
-        flakyDeltasByBuild: flakyDeltasByBuild
-    };
-}
-
-document.addEventListener('mousedown', function(e) {
-    // Clear the open popup, unless the click was inside the popup.
-    var popup = $('popup');
-    if (popup && e.target != popup && !(popup.compareDocumentPosition(e.target) & 16))
-        hidePopup();
-}, false);
-
-window.addEventListener('load', function() {
-    // This doesn't seem totally accurate as there is a race between
-    // onload firing and the last script tag being executed.
-    logTime('Time to load JS', g_pageLoadStartTime);
-    g_resourceLoader = new loader.Loader();
-    g_resourceLoader.load();
-}, false);
+    return g_history.isLayoutTestResults() ? LAYOUT_TEST_EXPECTATIONS_MAP_ : GTEST_EXPECTATIONS_MAP_;
+}
\ No newline at end of file
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.html b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.html
index 625d423..725bcc8 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.html
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.html
@@ -32,5 +32,8 @@
 <link rel="stylesheet" href="flakiness_dashboard_tests.css"></link>
 <script src="builders.js"></script>
 <script src="loader.js"></script>
+<script src="string.js"></script>
+<script src="history.js"></script>
 <script src="dashboard_base.js"></script>
+<script src="ui.js"></script>
 <script src="flakiness_dashboard.js"></script>
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
index 7589e34..510ecd5 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
@@ -134,28 +134,29 @@
     'platform/chromium/virtual/gpu/canvas/philip': 'canvas/philip'
 };
 
+var resourceLoader;
+
 //////////////////////////////////////////////////////////////////////////////
 // Methods and objects from dashboard_base.js to override.
 //////////////////////////////////////////////////////////////////////////////
 function generatePage()
 {
-    if (g_crossDashboardState.useTestData)
+    if (g_history.crossDashboardState.useTestData)
         return;
 
-    updateDefaultBuilderState();
     document.body.innerHTML = '<div id="loading-ui">LOADING...</div>';
-    showErrors();
+    resourceLoader.showErrors();
 
     // tests expands to all tests that match the CSV list.
     // result expands to all tests that ever have the given result
-    if (g_currentState.tests || g_currentState.result)
+    if (g_history.dashboardSpecificState.tests || g_history.dashboardSpecificState.result)
         generatePageForIndividualTests(individualTests());
-    else if (g_currentState.expectationsUpdate)
+    else if (g_history.dashboardSpecificState.expectationsUpdate)
         generatePageForExpectationsUpdate();
     else
-        generatePageForBuilder(g_currentState.builder);
+        generatePageForBuilder(g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder());
 
-    for (var builder in g_builders)
+    for (var builder in currentBuilders())
         processTestResultsForBuilderAsync(builder);
 
     postHeightChangedMessage();
@@ -165,15 +166,15 @@
 {
     switch(key) {
     case 'tests':
-        validateParameter(g_currentState, key, value,
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
             function() {
-                return isValidName(value);
+                return string.isValidName(value);
             });
         return true;
 
     case 'result':
         value = value.toUpperCase();
-        validateParameter(g_currentState, key, value,
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
             function() {
                 for (var result in LAYOUT_TEST_EXPECTATIONS_MAP_) {
                     if (value == LAYOUT_TEST_EXPECTATIONS_MAP_[result])
@@ -184,17 +185,18 @@
         return true;
 
     case 'builder':
-        validateParameter(g_currentState, key, value,
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
             function() {
-                return value in g_builders;
+                return value in currentBuilders();
             });
+
         return true;
 
     case 'sortColumn':
-        validateParameter(g_currentState, key, value,
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
             function() {
                 // Get all possible headers since the actual used set of headers
-                // depends on the values in g_currentState, which are currently being set.
+                // depends on the values in g_history.dashboardSpecificState, which are currently being set.
                 var headers = tableHeaders(true);
                 for (var i = 0; i < headers.length; i++) {
                     if (value == sortColumnFromTableHeader(headers[i]))
@@ -205,7 +207,7 @@
         return true;
 
     case 'sortOrder':
-        validateParameter(g_currentState, key, value,
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
             function() {
                 return value == FORWARD || value == BACKWARD;
             });
@@ -214,7 +216,7 @@
     case 'resultsHeight':
     case 'updateIndex':
     case 'revision':
-        validateParameter(g_currentState, key, Number(value),
+        history.validateParameter(g_history.dashboardSpecificState, key, Number(value),
             function() {
                 return value.match(/^\d+$/);
             });
@@ -232,7 +234,7 @@
     case 'showUnexpectedPasses':
     case 'showWontFixSkip':
     case 'expectationsUpdate':
-        g_currentState[key] = value == 'true';
+        g_history.dashboardSpecificState[key] = value == 'true';
         return true;
 
     default:
@@ -248,18 +250,25 @@
     showLargeExpectations: false,
     legacyExpectationsSemantics: true,
     showChrome: true,
-    showCorrectExpectations: !isLayoutTestResults(),
-    showWrongExpectations: !isLayoutTestResults(),
-    showWontFixSkip: !isLayoutTestResults(),
-    showSlow: !isLayoutTestResults(),
-    showSkipped: !isLayoutTestResults(),
-    showUnexpectedPasses: !isLayoutTestResults(),
+    showCorrectExpectations: !g_history.isLayoutTestResults(),
+    showWrongExpectations: !g_history.isLayoutTestResults(),
+    showWontFixSkip: !g_history.isLayoutTestResults(),
+    showSlow: !g_history.isLayoutTestResults(),
+    showSkipped: !g_history.isLayoutTestResults(),
+    showUnexpectedPasses: !g_history.isLayoutTestResults(),
     expectationsUpdate: false,
     updateIndex: 0,
     resultsHeight: 300,
     revision: null,
     tests: '',
     result: '',
+    builder: null
+};
+
+DB_SPECIFIC_INVALIDATING_PARAMETERS = {
+    'tests' : 'builder',
+    'testType': 'builder',
+    'group': 'builder'
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -325,56 +334,56 @@
 function matchingElement(stringToMatch, elementsMap)
 {
     for (var element in elementsMap) {
-        if (stringContains(stringToMatch, elementsMap[element]))
+        if (string.contains(stringToMatch, elementsMap[element]))
             return element;
     }
 }
 
 function determineWKPlatform(builderName, basePlatform)
 {
-    var isWK2Builder = stringContains(builderName, 'WK2') || stringContains(builderName, 'WEBKIT2');
+    var isWK2Builder = string.contains(builderName, 'WK2') || string.contains(builderName, 'WEBKIT2');
     return basePlatform + (isWK2Builder ? '_WK2' : '_WK1');
 }
 
 function nonChromiumPlatform(builderNameUpperCase)
 {
-    if (stringContains(builderNameUpperCase, 'WINDOWS 7'))
+    if (string.contains(builderNameUpperCase, 'WINDOWS 7'))
         return 'APPLE_WIN_WIN7';
-    if (stringContains(builderNameUpperCase, 'WINDOWS XP'))
+    if (string.contains(builderNameUpperCase, 'WINDOWS XP'))
         return 'APPLE_WIN_XP';
-    if (stringContains(builderNameUpperCase, 'QT LINUX'))
+    if (string.contains(builderNameUpperCase, 'QT LINUX'))
         return 'QT_LINUX';
 
-    if (stringContains(builderNameUpperCase, 'LION'))
+    if (string.contains(builderNameUpperCase, 'LION'))
         return determineWKPlatform(builderNameUpperCase, 'APPLE_MAC_LION');
-    if (stringContains(builderNameUpperCase, 'SNOWLEOPARD'))
+    if (string.contains(builderNameUpperCase, 'SNOWLEOPARD'))
         return determineWKPlatform(builderNameUpperCase, 'APPLE_MAC_SNOWLEOPARD');
-    if (stringContains(builderNameUpperCase, 'GTK LINUX'))
+    if (string.contains(builderNameUpperCase, 'GTK LINUX'))
         return determineWKPlatform(builderNameUpperCase, 'GTK_LINUX');
-    if (stringContains(builderNameUpperCase, 'EFL'))
+    if (string.contains(builderNameUpperCase, 'EFL'))
         return determineWKPlatform(builderNameUpperCase, 'EFL_LINUX');
 }
 
 function chromiumPlatform(builderNameUpperCase)
 {
-    if (stringContains(builderNameUpperCase, 'MAC')) {
-        if (stringContains(builderNameUpperCase, '10.7'))
+    if (string.contains(builderNameUpperCase, 'MAC')) {
+        if (string.contains(builderNameUpperCase, '10.7'))
             return 'CHROMIUM_LION';
         // The webkit.org 'Chromium Mac Release (Tests)' bot runs SnowLeopard.
         return 'CHROMIUM_SNOWLEOPARD';
     }
-    if (stringContains(builderNameUpperCase, 'WIN7'))
+    if (string.contains(builderNameUpperCase, 'WIN7'))
         return 'CHROMIUM_WIN7';
-    if (stringContains(builderNameUpperCase, 'VISTA'))
+    if (string.contains(builderNameUpperCase, 'VISTA'))
         return 'CHROMIUM_VISTA';
-    if (stringContains(builderNameUpperCase, 'WIN') || stringContains(builderNameUpperCase, 'XP'))
+    if (string.contains(builderNameUpperCase, 'WIN') || string.contains(builderNameUpperCase, 'XP'))
         return 'CHROMIUM_XP';
-    if (stringContains(builderNameUpperCase, 'LINUX'))
+    if (string.contains(builderNameUpperCase, 'LINUX'))
         return 'CHROMIUM_LUCID';
-    if (stringContains(builderNameUpperCase, 'ANDROID'))
+    if (string.contains(builderNameUpperCase, 'ANDROID'))
         return 'CHROMIUM_ANDROID';
     // The interactive bot is XP, but doesn't have an OS in it's name.
-    if (stringContains(builderNameUpperCase, 'INTERACTIVE'))
+    if (string.contains(builderNameUpperCase, 'INTERACTIVE'))
         return 'CHROMIUM_XP';
 }
 
@@ -385,7 +394,7 @@
         var builderNameUpperCase = builderName.toUpperCase();
         
         var platform = '';
-        if (isLayoutTestResults() && g_crossDashboardState.group == '@ToT - webkit.org' && !stringContains(builderNameUpperCase, 'CHROMIUM'))
+        if (g_history.isLayoutTestResults() && currentBuilderGroupName() == '@ToT - webkit.org' && !string.contains(builderNameUpperCase, 'CHROMIUM'))
             platform = nonChromiumPlatform(builderNameUpperCase);
         else
             platform = chromiumPlatform(builderNameUpperCase);
@@ -393,7 +402,7 @@
         if (!platform)
             console.error('Could not resolve platform for builder: ' + builderName);
 
-        var buildType = stringContains(builderNameUpperCase, 'DBG') || stringContains(builderNameUpperCase, 'DEBUG') ? 'DEBUG' : 'RELEASE';
+        var buildType = string.contains(builderNameUpperCase, 'DBG') || string.contains(builderNameUpperCase, 'DEBUG') ? 'DEBUG' : 'RELEASE';
         g_perBuilderPlatformAndBuildType[builderName] = {platform: platform, buildType: buildType};
     }
     return g_perBuilderPlatformAndBuildType[builderName];
@@ -479,7 +488,7 @@
 function getAllTestsTrie()
 {
     if (!g_allTestsTrie)
-        g_allTestsTrie = new TestTrie(g_builders, g_resultsByBuilder);
+        g_allTestsTrie = new TestTrie(currentBuilders(), g_resultsByBuilder);
 
     return g_allTestsTrie;
 }
@@ -489,10 +498,10 @@
 // tests in the directory.
 function individualTests()
 {
-    if (g_currentState.result)
-        return allTestsWithResult(g_currentState.result);
+    if (g_history.dashboardSpecificState.result)
+        return allTestsWithResult(g_history.dashboardSpecificState.result);
 
-    if (!g_currentState.tests)
+    if (!g_history.dashboardSpecificState.tests)
         return [];
 
     return individualTestsForSubstringList();
@@ -501,11 +510,11 @@
 function substringList()
 {
     // Convert windows slashes to unix slashes.
-    var tests = g_currentState.tests.replace(/\\/g, '/');
-    var separator = stringContains(tests, ' ') ? ' ' : ',';
+    var tests = g_history.dashboardSpecificState.tests.replace(/\\/g, '/');
+    var separator = string.contains(tests, ' ') ? ' ' : ',';
     var testList = tests.split(separator);
 
-    if (isLayoutTestResults())
+    if (g_history.isLayoutTestResults())
         return testList;
 
     var testListWithoutModifiers = [];
@@ -534,7 +543,7 @@
 
         var hasAnyMatches = false;
         getAllTestsTrie().forEach(function(triePath) {
-            if (caseInsensitiveContains(triePath, path)) {
+            if (string.caseInsensitiveContains(triePath, path)) {
                 testsMap[triePath] = 1;
                 hasAnyMatches = true;
             }
@@ -610,7 +619,7 @@
 {
     if (!g_allTestsByPlatformAndBuildType[platform][buildType]) {
         var tests = {};
-        for (var thisBuilder in g_builders) {
+        for (var thisBuilder in currentBuilders()) {
             var thisBuilderBuildInfo = platformAndBuildType(thisBuilder);
             if (thisBuilderBuildInfo.buildType == buildType && thisBuilderBuildInfo.platform == platform) {
                 addTestsForBuilder(thisBuilder, tests);
@@ -642,7 +651,7 @@
         return {bugs: '', modifiers: modifiers};
     for (var j = 0; j < bugs.length; j++)
         modifiers = modifiers.replace(bugs[j], '');
-    return {bugs: bugs.join(' '), modifiers: collapseWhitespace(trimString(modifiers))};
+    return {bugs: bugs.join(' '), modifiers: string.collapseWhitespace(string.trimString(modifiers))};
 }
 
 function populateExpectationsData(resultsObject)
@@ -656,7 +665,7 @@
     var filteredModifiers = filterBugs(expectations.modifiers);
     resultsObject.modifiers = filteredModifiers.modifiers;
     resultsObject.bugs = filteredModifiers.bugs;
-    resultsObject.isWontFixSkip = stringContains(expectations.modifiers, 'WONTFIX') || stringContains(expectations.modifiers, 'SKIP'); 
+    resultsObject.isWontFixSkip = string.contains(expectations.modifiers, 'WONTFIX') || string.contains(expectations.modifiers, 'SKIP'); 
 }
 
 function platformObjectForName(platformName)
@@ -685,8 +694,8 @@
     var expectations = [];
     var lines = data.split('\n');
     lines.forEach(function(line) {
-        line = trimString(line);
-        if (!line || startsWith(line, '#'))
+        line = string.trimString(line);
+        if (!line || string.startsWith(line, '#'))
             return;
 
         // This code mimics _tokenize_line_using_new_format() in
@@ -895,8 +904,8 @@
 
         // Test has expectations, but no result in the builders results.
         // This means it's either SKIP or passes on all builds.
-        if (!allTestsForPlatformAndBuildType[test] && !stringContains(expectations.modifiers, 'WONTFIX')) {
-            if (stringContains(expectations.modifiers, 'SKIP'))
+        if (!allTestsForPlatformAndBuildType[test] && !string.contains(expectations.modifiers, 'WONTFIX')) {
+            if (string.contains(expectations.modifiers, 'SKIP'))
                 skipped.push(test);
             else if (!expectations.expectations.match(/^\s*PASS\s*$/)) {
                 // Don't show tests expected to always pass. This is used in ways like
@@ -919,7 +928,7 @@
 
 function processTestRunsForAllBuilders()
 {
-    for (var builder in g_builders)
+    for (var builder in currentBuilders())
         processTestRunsForBuilder(builder);
 }
 
@@ -935,8 +944,7 @@
     }
 
     processExpectations();
-    var start = Date.now();
-
+   
     var buildInfo = platformAndBuildType(builderName);
     var platform = buildInfo.platform;
     var buildType = buildInfo.buildType;
@@ -989,7 +997,6 @@
     }
 
     g_perBuilderFailures[builderName] = failures;
-    logTime('processTestRunsForBuilder: ' + builderName, start);
 }
 
 function processMissingAndExtraExpectations(resultsForTest)
@@ -1048,14 +1055,14 @@
     var missingExpectations = [];
     var extraExpectations = [];
 
-    if (isLayoutTestResults()) {
+    if (g_history.isLayoutTestResults()) {
         var expectationsArray = resultsForTest.expectations ? resultsForTest.expectations.split(' ') : [];
         extraExpectations = expectationsArray.filter(
             function(element) {
                 // FIXME: Once all the FAIL lines are removed from
                 // TestExpectations, delete all the legacyExpectationsSemantics
                 // code.
-                if (g_currentState.legacyExpectationsSemantics) {
+                if (g_history.dashboardSpecificState.legacyExpectationsSemantics) {
                     if (element == 'FAIL') {
                         for (var i = 0; i < FAIL_RESULTS.length; i++) {
                             if (resultsMap[FAIL_RESULTS[i]])
@@ -1065,7 +1072,7 @@
                     }
                 }
 
-                return element && !resultsMap[element] && !stringContains(element, 'BUG');
+                return element && !resultsMap[element] && !string.contains(element, 'BUG');
             });
 
         for (var result in resultsMap) {
@@ -1076,7 +1083,7 @@
                 // FIXME: Once all the FAIL lines are removed from
                 // TestExpectations, delete all the legacyExpectationsSemantics
                 // code.
-                if (g_currentState.legacyExpectationsSemantics) {
+                if (g_history.dashboardSpecificState.legacyExpectationsSemantics) {
                     if (expectation == 'FAIL') {
                         for (var j = 0; j < FAIL_RESULTS.length; j++) {
                             if (result == FAIL_RESULTS[j]) {
@@ -1105,9 +1112,9 @@
         // hundred runs. It's not worth the manual maintenance effort.
         // Also, if a test times out, then it should not be marked as slow.
         var minTimeForNeedsSlow = isDebug(resultsForTest.builder) ? 2 : 1;
-        if (isSlowTest(resultsForTest) && !resultsMap['TIMEOUT'] && (!resultsForTest.modifiers || !stringContains(resultsForTest.modifiers, 'SLOW')))
+        if (isSlowTest(resultsForTest) && !resultsMap['TIMEOUT'] && (!resultsForTest.modifiers || !string.contains(resultsForTest.modifiers, 'SLOW')))
             missingExpectations.push('SLOW');
-        else if (isFastTest(resultsForTest) && resultsForTest.modifiers && stringContains(resultsForTest.modifiers, 'SLOW'))
+        else if (isFastTest(resultsForTest) && resultsForTest.modifiers && string.contains(resultsForTest.modifiers, 'SLOW'))
             extraExpectations.push('SLOW');
 
         // If there are no missing results or modifiers besides build
@@ -1148,7 +1155,7 @@
     return '<a href="' + url + '" target="_blank">' + text + '</a>';
 }
 
-// FIXME: replaced with chromiumRevisionLink/webKitRevisionLink
+// FIXME: replaced with ui.html.chromiumRevisionLink/ui.html.webKitRevisionLink
 function createBlameListHTML(revisions, index, urlBase, separator, repo)
 {
     var thisRevision = revisions[index];
@@ -1199,7 +1206,7 @@
 // Returns the path to the failure log for this non-webkit test.
 function pathToFailureLog(testName)
 {
-    return '/steps/' + g_crossDashboardState.testType + '/logs/' + testName.split('.')[1]
+    return '/steps/' + g_history.crossDashboardState.testType + '/logs/' + testName.split('.')[1]
 }
 
 function showPopupForBuild(e, builder, index, opt_testName)
@@ -1223,9 +1230,9 @@
             'WebKit') +
         '</li>';
 
-    if (master == WEBKIT_BUILDER_MASTER) {
+    if (master.name == WEBKIT_BUILDER_MASTER) {
         var revision = g_resultsByBuilder[builder].webkitRevision[index];
-        html += '<li><span class=link onclick="setQueryParameter(\'revision\',' +
+        html += '<li><span class=link onclick="g_history.setQueryParameter(\'revision\',' +
             revision + ')">Show results for WebKit r' + revision +
             '</span></li>';
     } else {
@@ -1235,17 +1242,17 @@
             '</li>';
 
         var chromeRevision = g_resultsByBuilder[builder].chromeRevision[index];
-        if (chromeRevision && isLayoutTestResults()) {
-            html += '<li><a href="' + TEST_RESULTS_BASE_PATH + g_builders[builder] +
+        if (chromeRevision && g_history.isLayoutTestResults()) {
+            html += '<li><a href="' + TEST_RESULTS_BASE_PATH + currentBuilders()[builder] +
                 '/' + chromeRevision + '/layout-test-results.zip">layout-test-results.zip</a></li>';
         }
     }
 
-    if (!isLayoutTestResults() && opt_testName && isFailure(builder, opt_testName, index))
+    if (!g_history.isLayoutTestResults() && opt_testName && isFailure(builder, opt_testName, index))
         html += '<li>' + linkHTMLToOpenWindow(buildBasePath + pathToFailureLog(opt_testName), 'Failure log') + '</li>';
 
     html += '</ul>';
-    showPopup(e.target, html);
+    ui.popup.show(e.target, html);
 }
 
 function htmlForTestResults(test)
@@ -1291,7 +1298,7 @@
         var extraClassNames = '';
         var webkitRevision = g_resultsByBuilder[builder].webkitRevision;
         var isWebkitMerge = webkitRevision[i + 1] && webkitRevision[i] != webkitRevision[i + 1];
-        if (isWebkitMerge && master != WEBKIT_BUILDER_MASTER)
+        if (isWebkitMerge && master.name != WEBKIT_BUILDER_MASTER)
             extraClassNames += ' merge';
 
         html += '<td title="' + (resultString || 'NO DATA') + '. Click for more info." class="results ' + currentResult +
@@ -1312,23 +1319,23 @@
     if (tests.length || skippedPaths.length) {
         var buildInfo = platformAndBuildType(builder);
         html += '<h2 style="display:inline-block">Expectations for ' + buildInfo.platform + '-' + buildInfo.buildType + '</h2> ';
-        if (!g_currentState.showUnexpectedPasses && tests.length)
+        if (!g_history.dashboardSpecificState.showUnexpectedPasses && tests.length)
             html += showUnexpectedPassesLink;
         html += ' ';
-        if (!g_currentState.showSkipped && skippedPaths.length)
+        if (!g_history.dashboardSpecificState.showSkipped && skippedPaths.length)
             html += showSkippedLink;
     }
 
     var open = '<div onclick="selectContents(this)">';
 
-    if (g_currentState.showUnexpectedPasses && tests.length) {
+    if (g_history.dashboardSpecificState.showUnexpectedPasses && tests.length) {
         html += '<div id="passing-tests">' + showUnexpectedPassesLink;
         for (var i = 0; i < tests.length; i++)
             html += open + tests[i].test + '</div>';
         html += '</div>';
     }
 
-    if (g_currentState.showSkipped && skippedPaths.length)
+    if (g_history.dashboardSpecificState.showSkipped && skippedPaths.length)
         html += '<div id="skipped-tests">' + showSkippedLink + open + skippedPaths.join('</div>' + open) + '</div></div>';
     return html + '<br>';
 }
@@ -1337,18 +1344,18 @@
 function shouldHideTest(testResult)
 {
     if (testResult.isWontFixSkip)
-        return !g_currentState.showWontFixSkip;
+        return !g_history.dashboardSpecificState.showWontFixSkip;
 
     if (testResult.isFlaky)
-        return !g_currentState.showFlaky;
+        return !g_history.dashboardSpecificState.showFlaky;
 
     if (isSlowTest(testResult))
-        return !g_currentState.showSlow;
+        return !g_history.dashboardSpecificState.showSlow;
 
     if (testResult.meetsExpectations)
-        return !g_currentState.showCorrectExpectations;
+        return !g_history.dashboardSpecificState.showCorrectExpectations;
 
-    return !g_currentState.showWrongExpectations;
+    return !g_history.dashboardSpecificState.showWrongExpectations;
 }
 
 // Sets the browser's selection to the element's contents.
@@ -1372,7 +1379,7 @@
 
 function isCrossBuilderView()
 {
-    return g_currentState.tests || g_currentState.result || g_currentState.expectationsUpdate;
+    return g_history.dashboardSpecificState.tests || g_history.dashboardSpecificState.result || g_history.dashboardSpecificState.expectationsUpdate;
 }
 
 function tableHeaders(opt_getAll)
@@ -1384,7 +1391,7 @@
     if (!isCrossBuilderView() || opt_getAll)
         headers.push('test');
 
-    if (isLayoutTestResults() || opt_getAll)
+    if (g_history.isLayoutTestResults() || opt_getAll)
         headers.push('bugs', 'modifiers', 'expectations');
 
     headers.push('slowest run', 'flakiness (numbers are runtimes in seconds)');
@@ -1405,23 +1412,23 @@
     var html = '';
     for (var i = 0; i < headers.length; i++) {
         var header = headers[i];
-        if (startsWith(header, 'test') || startsWith(header, 'builder')) {
+        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="setQueryParameter(\'tests\',\'' + test.test +'\');">' + test.test + '</span>';
+            var testCellHTML = isCrossBuilderView() ? test.builder : '<span class="link" onclick="g_history.setQueryParameter(\'tests\',\'' + test.test +'\');">' + test.test + '</span>';
 
             html += '<tr><td class="' + testCellClassName + '">' + testCellHTML;
-        } else if (startsWith(header, 'bugs'))
+        } else if (string.startsWith(header, 'bugs'))
             html += '<td class=options-container>' + (test.bugs ? htmlForBugs(test.bugs) : createBugHTML(test));
-        else if (startsWith(header, 'modifiers'))
+        else if (string.startsWith(header, 'modifiers'))
             html += '<td class=options-container>' + test.modifiers;
-        else if (startsWith(header, 'expectations'))
+        else if (string.startsWith(header, 'expectations'))
             html += '<td class=options-container>' + test.expectations;
-        else if (startsWith(header, 'slowest'))
+        else if (string.startsWith(header, 'slowest'))
             html += '<td>' + (test.slowestTime ? test.slowestTime + 's' : '');
-        else if (startsWith(header, 'flakiness'))
+        else if (string.startsWith(header, 'flakiness'))
             html += htmlForTestResults(test);
     }
     return html;
@@ -1436,8 +1443,8 @@
 {
     // Use the first word of the header title as the sortkey
     var thisSortValue = sortColumnFromTableHeader(headerName);
-    var arrowHTML = thisSortValue == g_currentState.sortColumn ?
-        '<span class=' + g_currentState.sortOrder + '>' + (g_currentState.sortOrder == FORWARD ? '&uarr;' : '&darr;' ) + '</span>' : '';
+    var arrowHTML = thisSortValue == g_history.dashboardSpecificState.sortColumn ?
+        '<span class=' + g_history.dashboardSpecificState.sortOrder + '>' + (g_history.dashboardSpecificState.sortOrder == FORWARD ? '&uarr;' : '&darr;' ) + '</span>' : '';
     return '<th sortValue=' + thisSortValue +
         // Extend last th through all the rest of the columns.
         (opt_fillColSpan ? ' colspan=10000' : '') +
@@ -1462,13 +1469,11 @@
 
 function appendHTML(html)
 {
-    var startTime = Date.now();
     // InnerHTML to a div that's not in the document. This is
     // ~300ms faster in Safari 4 and Chrome 4 on mac.
     var div = document.createElement('div');
     div.innerHTML = html;
     document.body.appendChild(div);
-    logTime('Time to innerHTML', startTime);
     postHeightChangedMessage();
 }
 
@@ -1515,12 +1520,12 @@
 
     var sort = 'sortColumn';
     var orderKey = 'sortOrder';
-    if (sortValue == g_currentState[sort] && g_currentState[orderKey] == FORWARD)
+    if (sortValue == g_history.dashboardSpecificState[sort] && g_history.dashboardSpecificState[orderKey] == FORWARD)
         order = BACKWARD;
     else
         order = FORWARD;
 
-    setQueryParameter(sort, sortValue, orderKey, order);
+    g_history.setQueryParameter(sort, sortValue, orderKey, order);
 }
 
 function sortTests(tests, column, order)
@@ -1575,7 +1580,7 @@
 {
     var modifiers = modifierString.split(' ');;
     return modifiers.filter(function(modifier) {
-        if (modifier in BUILD_TYPES || startsWith(modifier, 'BUG'))
+        if (modifier in BUILD_TYPES || string.startsWith(modifier, 'BUG'))
             return false;
 
         var matchesPlatformOrUnion = false;
@@ -1604,8 +1609,8 @@
 function generatePageForExpectationsUpdate()
 {
     // Always show all runs when auto-updating expectations.
-    if (!g_crossDashboardState.showAllRuns)
-        setQueryParameter('showAllRuns', true);
+    if (!g_history.crossDashboardState.showAllRuns)
+        g_history.setQueryParameter('showAllRuns', true);
 
     processTestRunsForAllBuilders();
     var testsNeedingUpdate = {};
@@ -1626,7 +1631,7 @@
         }
     }
 
-    for (var builder in g_builders) {
+    for (var builder in currentBuilders()) {
         var tests = g_perBuilderWithExpectationsButNoFailures[builder]
         for (var i = 0; i < tests.length; i++) {
             // Anything extra in this case is what is listed in expectations
@@ -1651,13 +1656,13 @@
 // @param {Array.<string>} keys Keys into the testNeedingUpdate object.
 function showUpdateInfoForTest(testsNeedingUpdate, keys)
 {
-    var test = keys[g_currentState.updateIndex];
+    var test = keys[g_history.dashboardSpecificState.updateIndex];
     document.body.innerHTML = '';
 
     // FIXME: Make this DOM creation less verbose.
     var index = document.createElement('div');
     index.style.cssFloat = 'right';
-    index.textContent = (g_currentState.updateIndex + 1) + ' of ' + keys.length + ' tests';
+    index.textContent = (g_history.dashboardSpecificState.updateIndex + 1) + ' of ' + keys.length + ' tests';
     document.body.appendChild(index);
 
     var buttonRegion = document.createElement('div');
@@ -1672,7 +1677,7 @@
     previousBtn.value = 'previous';
     previousBtn.addEventListener('click',
         function() {
-          setUpdateIndex(g_currentState.updateIndex - 1, testsNeedingUpdate, keys);
+          setUpdateIndex(g_history.dashboardSpecificState.updateIndex - 1, testsNeedingUpdate, keys);
         },
         false);
     buttonRegion.appendChild(previousBtn);
@@ -1734,7 +1739,7 @@
 // @param {Array.<string>} keys Keys into the testNeedingUpdate object.
 function handleUpdate(testsNeedingUpdate, keys)
 {
-    var test = keys[g_currentState.updateIndex];
+    var test = keys[g_history.dashboardSpecificState.updateIndex];
     var updates = testsNeedingUpdate[test];
     for (var builder in updates) {
         // Add included tests, and delete excluded tests if
@@ -1758,7 +1763,7 @@
 // @param {Array.<string>} keys Keys into the testNeedingUpdate object.
 function nextUpdate(testsNeedingUpdate, keys)
 {
-    setUpdateIndex(g_currentState.updateIndex + 1, testsNeedingUpdate, keys);
+    setUpdateIndex(g_history.dashboardSpecificState.updateIndex + 1, testsNeedingUpdate, keys);
 }
 
 
@@ -1773,7 +1778,7 @@
         newIndex = keys.length - 1;
     else if (newIndex == keys.length)
         newIndex = 0;
-    setQueryParameter("updateIndex", newIndex);
+    g_history.setQueryParameter("updateIndex", newIndex);
     showUpdateInfoForTest(testsNeedingUpdate, keys);
 }
 
@@ -1793,7 +1798,7 @@
     }
 
     var skippedBuilders = []
-    for (builder in currentBuilderGroup().builders) {
+    for (builder in currentBuilders()) {
         if (shownBuilders.indexOf(builder) == -1)
             skippedBuilders.push(builder);
     }
@@ -1818,12 +1823,12 @@
     html += '<div class=expectations test=' + test + '><div>' +
         linkHTMLToToggleState('showExpectations', 'results')
 
-    if (isLayoutTestResults() || isGPUTestResults()) {
-        if (isLayoutTestResults())
+    if (g_history.isLayoutTestResults() || g_history.isGPUTestResults()) {
+        if (g_history.isLayoutTestResults())
             html += ' | ' + linkHTMLToToggleState('showLargeExpectations', 'large thumbnails');
-        if (testResults && builderMaster(testResults[0].builder) == WEBKIT_BUILDER_MASTER) {
-            var revision = g_currentState.revision || '';
-            html += '<form onsubmit="setQueryParameter(\'revision\', revision.value);' +
+        if (testResults && currentBuilderGroup().master().name == WEBKIT_BUILDER_MASTER) {
+            var revision = g_history.dashboardSpecificState.revision || '';
+            html += '<form onsubmit="g_history.setQueryParameter(\'revision\', revision.value);' +
                 'return false;">Show results for WebKit revision: ' +
                 '<input name=revision placeholder="e.g. 65540" value="' + revision +
                 '" id=revision-input></form>';
@@ -1831,8 +1836,8 @@
             html += ' | <b>Only shows actual results/diffs from the most recent *failure* on each bot.</b>';
     } else {
       html += ' | <span>Results height:<input ' +
-          'onchange="setQueryParameter(\'resultsHeight\',this.value)" value="' +
-          g_currentState.resultsHeight + '" style="width:2.5em">px</span>';
+          'onchange="g_history.setQueryParameter(\'resultsHeight\',this.value)" value="' +
+          g_history.dashboardSpecificState.resultsHeight + '" style="width:2.5em">px</span>';
     }
     html += '</div></div>';
     return html;
@@ -1922,21 +1927,21 @@
         childContainer.appendChild(expectationsTitle(platformPart + suitePart, path, opt_builder));
         childContainer.className = 'expectations-item';
         item.className = 'expectation ' + fileExtension;
-        if (g_currentState.showLargeExpectations)
+        if (g_history.dashboardSpecificState.showLargeExpectations)
             item.className += ' large';
         childContainer.appendChild(item);
         handleFinishedLoadingExpectations(container);
     };
 
     var url = base + platformPart + path;
-    if (isImage || !startsWith(base, 'http://svn.webkit.org')) {
+    if (isImage || !string.startsWith(base, 'http://svn.webkit.org')) {
         var dummyNode = document.createElement(isImage ? 'img' : 'script');
         dummyNode.src = url;
         dummyNode.onload = function() {
             var item;
             if (isImage) {
                 item = dummyNode;
-                if (startsWith(base, 'http://svn.webkit.org'))
+                if (string.startsWith(base, 'http://svn.webkit.org'))
                     maybeAddPngChecksum(item, url);
             } else {
                 item = document.createElement('iframe');
@@ -2030,11 +2035,11 @@
         platforms['WIN'] = {};
         platforms['LINUX'] = {};
         allPlatforms.forEach(function(platform) {
-            if (startsWith(platform, 'MAC'))
+            if (string.startsWith(platform, 'MAC'))
                 platforms['MAC'][platform] = 1;
-            else if (startsWith(platform, 'WIN'))
+            else if (string.startsWith(platform, 'WIN'))
                 platforms['WIN'][platform] = 1;
-            else if (startsWith(platform, 'LINUX'))
+            else if (string.startsWith(platform, 'LINUX'))
                 platforms['LINUX'][platform] = 1;
         });
 
@@ -2048,7 +2053,7 @@
                 var nodesToRemove = [];
                 for (var j = 0, usedPlatformsLength = usedPlatforms.length; j < usedPlatformsLength; j++) {
                     var usedPlatform = usedPlatforms[j];
-                    if (startsWith(usedPlatform.textContent, platform))
+                    if (string.startsWith(usedPlatform.textContent, platform))
                         nodesToRemove.push(usedPlatform);
                 }
 
@@ -2077,11 +2082,11 @@
     var innerHTML;
     if (builder) {
         var resultsType;
-        if (endsWith(path, '-crash-log.txt'))
+        if (string.endsWith(path, '-crash-log.txt'))
             resultsType = 'STACKTRACE';
-        else if (endsWith(path, '-actual.txt') || endsWith(path, '-actual.png'))
+        else if (string.endsWith(path, '-actual.txt') || string.endsWith(path, '-actual.png'))
             resultsType = 'ACTUAL RESULTS';
-        else if (endsWith(path, '-wdiff.html'))
+        else if (string.endsWith(path, '-wdiff.html'))
             resultsType = 'WDIFF';
         else
             resultsType = 'DIFF';
@@ -2103,12 +2108,12 @@
 function loadExpectations(expectationsContainer)
 {
     var test = expectationsContainer.getAttribute('test');
-    if (isLayoutTestResults())
+    if (g_history.isLayoutTestResults())
         loadExpectationsLayoutTests(test, expectationsContainer);
     else {
         var results = g_testToResultsMap[test];
         for (var i = 0; i < results.length; i++)
-            if (isGPUTestResults())
+            if (g_history.isGPUTestResults())
                 loadGPUResultsForBuilder(results[i].builder, test, expectationsContainer);
             else
                 loadNonWebKitResultsForBuilder(results[i].builder, test, expectationsContainer);
@@ -2168,7 +2173,7 @@
         var item = document.createElement('iframe');
         item.src = dummyNode.src;
         item.className = itemClassName;
-        item.style.height = g_currentState.resultsHeight + 'px';
+        item.style.height = g_history.dashboardSpecificState.resultsHeight + 'px';
 
         if (opt_title) {
             var childContainer = document.createElement('div');
@@ -2255,9 +2260,9 @@
     var revisionContainer = document.createElement('div');
     revisionContainer.textContent = "Showing results for: "
     expectationsContainer.appendChild(revisionContainer);
-    for (var builder in g_builders) {
-        if (builderMaster(builder) == WEBKIT_BUILDER_MASTER) {
-            var latestRevision = g_currentState.revision || g_resultsByBuilder[builder].webkitRevision[0];
+    for (var builder in currentBuilders()) {
+        if (builderMaster(builder).name == WEBKIT_BUILDER_MASTER) {
+            var latestRevision = g_history.dashboardSpecificState.revision || g_resultsByBuilder[builder].webkitRevision[0];
             var buildInfo = buildInfoForRevision(builder, latestRevision);
             var revisionInfo = document.createElement('div');
             revisionInfo.style.cssText = 'background:lightgray;margin:0 3px;padding:0 2px;display:inline-block;';
@@ -2272,15 +2277,15 @@
     var testWithoutSuffix = test.substring(0, test.lastIndexOf('.'));
     var actualResultSuffixes = ['-actual.txt', '-actual.png', '-crash-log.txt', '-diff.txt', '-wdiff.html', '-diff.png'];
 
-    for (var builder in g_builders) {
+    for (var builder in currentBuilders()) {
         var actualResultsBase;
-        if (builderMaster(builder) == WEBKIT_BUILDER_MASTER) {
-            var latestRevision = g_currentState.revision || g_resultsByBuilder[builder].webkitRevision[0];
+        if (builderMaster(builder).name == WEBKIT_BUILDER_MASTER) {
+            var latestRevision = g_history.dashboardSpecificState.revision || g_resultsByBuilder[builder].webkitRevision[0];
             var buildInfo = buildInfoForRevision(builder, latestRevision);
             actualResultsBase = 'http://build.webkit.org/results/' + builder +
                 '/r' + buildInfo.revisionStart + ' (' + buildInfo.buildNumber + ')/';
         } else
-            actualResultsBase = TEST_RESULTS_BASE_PATH + g_builders[builder] + '/results/layout-test-results/';
+            actualResultsBase = TEST_RESULTS_BASE_PATH + currentBuilders()[builder] + '/results/layout-test-results/';
 
         for (var i = 0; i < actualResultSuffixes.length; i++) {
             addExpectationItem(expectationsContainers, expectationsContainer, null,
@@ -2324,7 +2329,7 @@
 
 function appendExpectations()
 {
-    var expectations = g_currentState.showExpectations ? document.getElementsByClassName('expectations') : [];
+    var expectations = g_history.dashboardSpecificState.showExpectations ? document.getElementsByClassName('expectations') : [];
     // Loading expectations is *very* slow. Use a large timeout to avoid
     // totally hanging the renderer.
     performChunkedAction(expectations, function(chunk) {
@@ -2346,13 +2351,13 @@
 function generatePageForIndividualTests(tests)
 {
     console.log('Number of tests: ' + tests.length);
-    if (g_currentState.showChrome)
+    if (g_history.dashboardSpecificState.showChrome)
         appendHTML(htmlForNavBar());
     performChunkedAction(tests, function(chunk) {
         appendHTML(htmlForIndividualTests(chunk));
     }, appendExpectations, 500);
-    if (g_currentState.showChrome)
-        $('tests-input').value = g_currentState.tests;
+    if (g_history.dashboardSpecificState.showChrome)
+        $('tests-input').value = g_history.dashboardSpecificState.tests;
 }
 
 function performChunkedAction(tests, handleChunk, onComplete, timeout, opt_index) {
@@ -2374,8 +2379,8 @@
     for (var i = 0; i < tests.length; i++) {
         var test = tests[i];
         var testNameHtml = '';
-        if (g_currentState.showChrome || tests.length > 1) {
-            if (isLayoutTestResults()) {
+        if (g_history.dashboardSpecificState.showChrome || tests.length > 1) {
+            if (g_history.isLayoutTestResults()) {
                 var suite = lookupVirtualTestSuite(test);
                 var base = suite ? baseTest(test, suite) : test;
                 var tracURL = TEST_URL_BASE_PATH_TRAC + base;
@@ -2392,13 +2397,13 @@
 function htmlForNavBar()
 {
     var extraHTML = '';
-    var html = htmlForTestTypeSwitcher(false, extraHTML, isCrossBuilderView());
+    var html = ui.html.testTypeSwitcher(false, extraHTML, isCrossBuilderView());
     html += '<div class=forms><form id=result-form ' +
-        'onsubmit="setQueryParameter(\'result\', result.value);' +
+        'onsubmit="g_history.setQueryParameter(\'result\', result.value);' +
         'return false;">Show all tests with result: ' +
         '<input name=result placeholder="e.g. CRASH" id=result-input>' +
         '</form><form id=tests-form ' +
-        'onsubmit="setQueryParameter(\'tests\', tests.value);' +
+        'onsubmit="g_history.setQueryParameter(\'tests\', tests.value);' +
         'return false;"><span>Show tests on all platforms: </span>' +
         '<input name=tests ' +
         'placeholder="Comma or space-separated list of tests or partial ' +
@@ -2410,14 +2415,14 @@
 
 function checkBoxToToggleState(key, text)
 {
-    var stateEnabled = g_currentState[key];
-    return '<label><input type=checkbox ' + (stateEnabled ? 'checked ' : '') + 'onclick="setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + text + '</label> ';
+    var stateEnabled = g_history.dashboardSpecificState[key];
+    return '<label><input type=checkbox ' + (stateEnabled ? 'checked ' : '') + 'onclick="g_history.setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + text + '</label> ';
 }
 
 function linkHTMLToToggleState(key, linkText)
 {
-    var stateEnabled = g_currentState[key];
-    return '<span class=link onclick="setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + (stateEnabled ? 'Hide' : 'Show') + ' ' + linkText + '</span>';
+    var stateEnabled = g_history.dashboardSpecificState[key];
+    return '<span class=link onclick="g_history.setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + (stateEnabled ? 'Hide' : 'Show') + ' ' + linkText + '</span>';
 }
 
 function headerForTestTableHtml()
@@ -2435,7 +2440,7 @@
     processTestRunsForBuilder(builderName);
 
     var results = g_perBuilderFailures[builderName];
-    sortTests(results, g_currentState.sortColumn, g_currentState.sortOrder);
+    sortTests(results, g_history.dashboardSpecificState.sortColumn, g_history.dashboardSpecificState.sortOrder);
 
     var testsHTML = '';
     if (results.length) {
@@ -2445,7 +2450,7 @@
         testsHTML = htmlForTestTable(tableRowsHTML);
     } else {
         testsHTML = '<div>No tests found. ';
-        if (isLayoutTestResults())
+        if (g_history.isLayoutTestResults())
             testsHTML += 'Try showing tests with correct expectations.</div>';
         else
             testsHTML += 'This means no tests have failed!</div>';
@@ -2453,7 +2458,7 @@
 
     var html = htmlForNavBar();
 
-    if (isLayoutTestResults())
+    if (g_history.isLayoutTestResults())
         html += htmlForTestsWithExpectationsButNoFailures(builderName) + headerForTestTableHtml();
 
     html += '<br>' + testsHTML;
@@ -2481,15 +2486,7 @@
 
 function isInvalidKeyForCrossBuilderView(key)
 {
-    return !(key in VALID_KEYS_FOR_CROSS_BUILDER_VIEW) && !(key in g_defaultCrossDashboardStateValues);
-}
-
-function updateDefaultBuilderState()
-{
-    if (isCrossBuilderView())
-        delete g_defaultDashboardSpecificStateValues.builder;
-    else
-        g_defaultDashboardSpecificStateValues.builder = g_defaultBuilderName;
+    return !(key in VALID_KEYS_FOR_CROSS_BUILDER_VIEW) && !(key in history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES);
 }
 
 // Sets the page state to regenerate the page.
@@ -2499,18 +2496,17 @@
     for (key in params) {
         if (key == 'tests') {
             // Entering cross-builder view, only keep valid keys for that view.
-            for (var currentKey in g_currentState) {
+            for (var currentKey in g_history.dashboardSpecificState) {
               if (isInvalidKeyForCrossBuilderView(currentKey)) {
-                delete g_currentState[currentKey];
+                delete g_history.dashboardSpecificState[currentKey];
               }
             }
         } else if (isInvalidKeyForCrossBuilderView(key)) {
-            delete g_currentState.tests;
-            delete g_currentState.result;
+            delete g_history.dashboardSpecificState.tests;
+            delete g_history.dashboardSpecificState.result;
         }
     }
 
-    updateDefaultBuilderState();
     return true;
 }
 
@@ -2549,7 +2545,7 @@
         html += '<div class=' + expectation + '>' + expectationsMap()[expectation] + '</div>';
 
     html += '<div class=merge>WEBKIT MERGE</div>';
-    if (isLayoutTestResults()) {
+    if (g_history.isLayoutTestResults()) {
       html += '</div><br style="clear:both">' +
           '</div><h3>Test expectatons fallback order.</h3>';
 
@@ -2592,7 +2588,7 @@
 }
 
 if (window != parent)
-    window.addEventListener('blur', hidePopup);
+    window.addEventListener('blur', ui.popup.hide);
 
 document.addEventListener('keydown', function(e) {
     if (e.keyIdentifier == 'U+003F' || e.keyIdentifier == 'U+00BF') {
@@ -2602,6 +2598,11 @@
     } else if (e.keyIdentifier == 'U+001B') {
         // escape key
         hideLegend();
-        hidePopup();
+        ui.popup.hide();
     }
 }, false);
+
+window.addEventListener('load', function() {
+    resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_embedded_unittests.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_embedded_unittests.js
index 5599b3e..039467a 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_embedded_unittests.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_embedded_unittests.js
@@ -27,7 +27,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 test('hidePopupOnBlur', 2, function() {
-    showPopup(document.body, 'dummy content');
+    ui.popup.show(document.body, 'dummy content');
     ok(document.querySelector('#popup'));
 
     // Cause the window to be blurred.
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
index 597c62e..95247d9 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
@@ -26,30 +26,57 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+// FIXME(jparent): Rename this once it isn't globals.
 function resetGlobals()
 {
     allExpectations = null;
     allTests = null;
     g_expectationsByPlatform = {};
     g_resultsByBuilder = {};
-    g_builders = {};
     g_allExpectations = null;
     g_allTestsTrie = null;
-    g_currentState = {};
-    g_crossDashboardState = {};
-    for (var key in g_defaultCrossDashboardStateValues)
-        g_crossDashboardState[key] = g_defaultCrossDashboardStateValues[key];
+    var historyInstance = new history.History();
+    // FIXME(jparent): Remove this once global isn't used.
+    g_history = historyInstance;
+    g_testToResultsMap = {};
+
+    for (var key in history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES)
+        historyInstance.crossDashboardState[key] = history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES[key];
+
+    LOAD_BUILDBOT_DATA([{
+        name: 'ChromiumWebkit',
+        url: 'dummyurl', 
+        tests: {'layout-tests': {'builders': ['WebKit Linux', 'WebKit Linux (dbg)', 'WebKit Mac10.7', 'WebKit Win', 'WebKit Win (dbg)']}}
+    },
+    {
+        name: 'webkit.org',
+        url: 'dummyurl',
+        tests: {'layout-tests': {'builders': ['Apple SnowLeopard Tests', 'Qt Linux Tests', 'Chromium Mac10.7 Tests', 'GTK Win']}}
+    }]);
+ 
+    for (var group in LAYOUT_TESTS_BUILDER_GROUPS)
+        LAYOUT_TESTS_BUILDER_GROUPS[group] = null;
+
+    return historyInstance;
+}
+
+function stubResultsByBuilder(data)
+{
+    for (var builder in currentBuilders())
+    {
+        g_resultsByBuilder[builder] = data[builder] || {'tests': []};
+    };
 }
 
 function runExpectationsTest(builder, test, expectations, modifiers)
 {
-    g_builders[builder] = true;
-
     // Put in some dummy results. processExpectations expects the test to be
     // there.
     var tests = {};
     tests[test] = {'results': [[100, 'F']], 'times': [[100, 0]]};
-    g_resultsByBuilder[builder] = {'tests': tests};
+    var results = {};
+    results[builder] = {'tests': tests};
+    stubResultsByBuilder(results);
 
     processExpectations();
     var resultsForTest = createResultsObjectForTest(test, builder);
@@ -60,25 +87,10 @@
     equal(resultsForTest.modifiers, modifiers, message);
 }
 
-test('flattenTrie', 1, function() {
-    resetGlobals();
-    var tests = {
-        'bar.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
-        'foo': {
-            'bar': {
-                'baz.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
-            }
-        }
-    };
-    var expectedFlattenedTests = {
-        'bar.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
-        'foo/bar/baz.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
-    };
-    equal(JSON.stringify(flattenTrie(tests)), JSON.stringify(expectedFlattenedTests))
-});
-
 test('releaseFail', 2, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+
     var builder = 'WebKit Win';
     var test = 'foo/1.html';
     var expectationsArray = [
@@ -90,6 +102,7 @@
 
 test('releaseFailDebugCrashReleaseBuilder', 2, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     var builder = 'WebKit Win';
     var test = 'foo/1.html';
     var expectationsArray = [
@@ -103,6 +116,7 @@
 
 test('releaseFailDebugCrashDebugBuilder', 2, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     var builder = 'WebKit Win (dbg)';
     var test = 'foo/1.html';
     var expectationsArray = [
@@ -116,6 +130,7 @@
 
 test('overrideJustBuildType', 12, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     var test = 'bar/1.html';
     g_expectationsByPlatform['CHROMIUM'] = getParsedExpectations('bar [ WontFix Failure Pass Timeout ]\n' +
         '[ Mac ] ' + test + ' [ WontFix Failure ]\n' +
@@ -130,6 +145,11 @@
 });
 
 test('platformAndBuildType', 78, function() {
+    var historyInstance = new history.History();
+    // FIXME(jparent): Change to use the flakiness_db's history object
+    // once it exists, rather than tracking global.
+    g_history = historyInstance;
+
     var runPlatformAndBuildTypeTest = function(builder, expectedPlatform, expectedBuildType) {
         g_perBuilderPlatformAndBuildType = {};
         buildInfo = platformAndBuildType(builder);
@@ -159,8 +179,8 @@
     runPlatformAndBuildTypeTest('XP Tests', 'CHROMIUM_XP', 'RELEASE');
     runPlatformAndBuildTypeTest('Interactive Tests (dbg)', 'CHROMIUM_XP', 'DEBUG');
     
-    g_crossDashboardState.group = '@ToT - webkit.org';
-    g_crossDashboardState.testType = 'layout-tests';
+    historyInstance.crossDashboardState.group = '@ToT - webkit.org';
+    historyInstance.crossDashboardState.testType = 'layout-tests';
     runPlatformAndBuildTypeTest('Chromium Win Release (Tests)', 'CHROMIUM_XP', 'RELEASE');
     runPlatformAndBuildTypeTest('Chromium Linux Release (Tests)', 'CHROMIUM_LUCID', 'RELEASE');
     runPlatformAndBuildTypeTest('Chromium Mac Release (Tests)', 'CHROMIUM_SNOWLEOPARD', 'RELEASE');
@@ -212,9 +232,10 @@
 
 test('getExpectations', 16, function() {
     resetGlobals();
-    g_builders['WebKit Win'] = true;
-    g_resultsByBuilder = {
-        'WebKit Win': {
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+ 
+    stubResultsByBuilder({
+        'WebKit Win' : {
             'tests': {
                 'foo/test1.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
                 'foo/test2.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
@@ -222,7 +243,7 @@
                 'test1.html': {'results': [[100, 'F']], 'times': [[100, 0]]}
             }
         }
-    }
+    });
 
     g_expectationsByPlatform['CHROMIUM'] = getParsedExpectations('Bug(123) foo [ Failure Pass Crash ]\n' +
         'Bug(Foo) [ Release ] foo/test1.html [ Failure ]\n' +
@@ -291,34 +312,41 @@
 });
 
 test('substringList', 2, function() {
-    g_crossDashboardState.testType = 'gtest';
-    g_currentState.tests = 'test.FLAKY_foo test.FAILS_foo1 test.DISABLED_foo2 test.MAYBE_foo3 test.foo4';
+    var historyInstance = new history.History();
+    // 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');
 
-    g_crossDashboardState.testType = 'layout-tests';
-    g_currentState.tests = 'foo/bar.FLAKY_foo.html';
+    historyInstance.crossDashboardState.testType = 'layout-tests';
+    historyInstance.dashboardSpecificState.tests = 'foo/bar.FLAKY_foo.html';
     equal(substringList().toString(), 'foo/bar.FLAKY_foo.html');
 });
 
 test('htmlForTestsWithExpectationsButNoFailures', 4, function() {
+    var historyInstance = new history.History();
+    // FIXME(jparent): Remove this once global isn't used.
+    g_history = historyInstance;
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     var builder = 'WebKit Win';
     g_perBuilderWithExpectationsButNoFailures[builder] = ['passing-test1.html', 'passing-test2.html'];
     g_perBuilderSkippedPaths[builder] = ['skipped-test1.html'];
     g_resultsByBuilder[builder] = { buildNumbers: [5, 4, 3, 1] };
 
-    g_currentState.showUnexpectedPasses = true;
-    g_currentState.showSkipped = true;
+    historyInstance.dashboardSpecificState.showUnexpectedPasses = true;
+    historyInstance.dashboardSpecificState.showSkipped = true;
 
-    g_crossDashboardState.group = '@ToT - chromium.org';
-    g_crossDashboardState.testType = 'layout-tests';
+    historyInstance.crossDashboardState.group = '@ToT - chromium.org';
+    historyInstance.crossDashboardState.testType = 'layout-tests';
     
     var container = document.createElement('div');
     container.innerHTML = htmlForTestsWithExpectationsButNoFailures(builder);
     equal(container.querySelectorAll('#passing-tests > div').length, 2);
     equal(container.querySelectorAll('#skipped-tests > div').length, 1);
     
-    g_currentState.showUnexpectedPasses = false;
-    g_currentState.showSkipped = false;
+    historyInstance.dashboardSpecificState.showUnexpectedPasses = false;
+    historyInstance.dashboardSpecificState.showSkipped = false;
     
     var container = document.createElement('div');
     container.innerHTML = htmlForTestsWithExpectationsButNoFailures(builder);
@@ -333,17 +361,20 @@
 });
 
 test('htmlForTestTypeSwitcherGroup', 6, function() {
+    var historyInstance = new history.History();
+    // FIXME(jparent): Remove this once global isn't used.
+    g_history = historyInstance;
     var container = document.createElement('div');
-    g_crossDashboardState.testType = 'ui_tests';
-    container.innerHTML = htmlForTestTypeSwitcher(true);
+    historyInstance.crossDashboardState.testType = 'ui_tests';
+    container.innerHTML = ui.html.testTypeSwitcher(true);
     var selects = container.querySelectorAll('select');
     equal(selects.length, 2);
     var group = selects[1];
     equal(group.parentNode.textContent.indexOf('Group:'), 0);
     equal(group.children.length, 3);
 
-    g_crossDashboardState.testType = 'layout-tests';
-    container.innerHTML = htmlForTestTypeSwitcher(true);
+    historyInstance.crossDashboardState.testType = 'layout-tests';
+    container.innerHTML = ui.html.testTypeSwitcher(true);
     var selects = container.querySelectorAll('select');
     equal(selects.length, 2);
     var group = selects[1];
@@ -353,17 +384,19 @@
 
 test('htmlForIndividualTestOnAllBuilders', 1, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     equal(htmlForIndividualTestOnAllBuilders('foo/nonexistant.html'), '<div class="not-found">Test not found. Either it does not exist, is skipped or passes on all platforms.</div>');
 });
 
 test('htmlForIndividualTestOnAllBuildersWithResultsLinksNonexistant', 1, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     equal(htmlForIndividualTestOnAllBuildersWithResultsLinks('foo/nonexistant.html'),
         '<div class="not-found">Test not found. Either it does not exist, is skipped or passes on all platforms.</div>' +
         '<div class=expectations test=foo/nonexistant.html>' +
             '<div>' +
-                '<span class=link onclick="setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
-                '<span class=link onclick="setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span> | ' +
+                '<span class=link onclick="g_history.setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
+                '<span class=link onclick="g_history.setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span> | ' +
                 '<b>Only shows actual results/diffs from the most recent *failure* on each bot.</b>' +
             '</div>' +
         '</div>');
@@ -371,9 +404,10 @@
 
 test('htmlForIndividualTestOnAllBuildersWithResultsLinks', 1, function() {
     resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+
+    var builderName = 'WebKit Linux';
     var test = 'dummytest.html';
-    var builderName = 'dummyBuilder';
-    BUILDER_TO_MASTER[builderName] = CHROMIUM_BUILDER_MASTER;
     g_testToResultsMap[test] = [createResultsObjectForTest(test, builderName)];
 
     equal(htmlForIndividualTestOnAllBuildersWithResultsLinks(test),
@@ -389,21 +423,22 @@
         '</table>' +
         '<div>The following builders either don\'t run this test (e.g. it\'s skipped) or all runs passed:</div>' +
         '<div class=skipped-builder-list>' +
-            '<div class=skipped-builder>WebKit Linux</div><div class=skipped-builder>WebKit Linux (dbg)</div>' +
-            '<div class=skipped-builder>WebKit Mac10.7</div><div class=skipped-builder>WebKit Win</div>' +
+            '<div class=skipped-builder>WebKit Linux (dbg)</div><div class=skipped-builder>WebKit Mac10.7</div><div class=skipped-builder>WebKit Win</div><div class=skipped-builder>WebKit Win (dbg)</div>' +
         '</div>' +
         '<div class=expectations test=dummytest.html>' +
-            '<div><span class=link onclick="setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
-            '<span class=link onclick="setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span> | ' +
+            '<div><span class=link onclick="g_history.setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
+            '<span class=link onclick="g_history.setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span> | ' +
             '<b>Only shows actual results/diffs from the most recent *failure* on each bot.</b></div>' +
         '</div>');
 });
 
 test('htmlForIndividualTestOnAllBuildersWithResultsLinksWebkitMaster', 1, function() {
-    resetGlobals();
+    var historyInstance = resetGlobals();
+    historyInstance.crossDashboardState.group = '@ToT - webkit.org';
+    loadBuildersList('@ToT - webkit.org', 'layout-tests');
+
+    var builderName = 'Apple SnowLeopard Tests';
     var test = 'dummytest.html';
-    var builderName = 'dummyBuilder';
-    BUILDER_TO_MASTER[builderName] = WEBKIT_BUILDER_MASTER;
     g_testToResultsMap[test] = [createResultsObjectForTest(test, builderName)];
 
     equal(htmlForIndividualTestOnAllBuildersWithResultsLinks(test),
@@ -419,52 +454,52 @@
         '</table>' +
         '<div>The following builders either don\'t run this test (e.g. it\'s skipped) or all runs passed:</div>' +
         '<div class=skipped-builder-list>' +
-            '<div class=skipped-builder>WebKit Linux</div><div class=skipped-builder>WebKit Linux (dbg)</div>' +
-            '<div class=skipped-builder>WebKit Mac10.7</div><div class=skipped-builder>WebKit Win</div>' +
+            '<div class=skipped-builder>Qt Linux Tests</div><div class=skipped-builder>Chromium Mac10.7 Tests</div><div class=skipped-builder>GTK Win</div>' +
         '</div>' +
         '<div class=expectations test=dummytest.html>' +
-            '<div><span class=link onclick="setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
-            '<span class=link onclick="setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span>' +
-            '<form onsubmit="setQueryParameter(\'revision\', revision.value);return false;">' +
+            '<div><span class=link onclick="g_history.setQueryParameter(\'showExpectations\', true)">Show results</span> | ' +
+            '<span class=link onclick="g_history.setQueryParameter(\'showLargeExpectations\', true)">Show large thumbnails</span>' +
+            '<form onsubmit="g_history.setQueryParameter(\'revision\', revision.value);return false;">' +
                 'Show results for WebKit revision: <input name=revision placeholder="e.g. 65540" value="" id=revision-input>' +
             '</form></div>' +
         '</div>');
 });
 
 test('htmlForIndividualTests', 4, function() {
-    resetGlobals();
+    var historyInstance = resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
     var test1 = 'foo/nonexistant.html';
     var test2 = 'bar/nonexistant.html';
 
-    g_currentState.showChrome = false;
+    historyInstance.dashboardSpecificState.showChrome = false;
 
     var tests = [test1, test2];
     equal(htmlForIndividualTests(tests),
         '<h2><a href="http://trac.webkit.org/browser/trunk/LayoutTests/foo/nonexistant.html" target="_blank">foo/nonexistant.html</a></h2>' +
         htmlForIndividualTestOnAllBuilders(test1) + 
         '<div class=expectations test=foo/nonexistant.html>' +
-            '<div><span class=link onclick=\"setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
-            '<span class=link onclick=\"setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
+            '<div><span class=link onclick=\"g_history.setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
+            '<span class=link onclick=\"g_history.setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
             '<b>Only shows actual results/diffs from the most recent *failure* on each bot.</b></div>' +
         '</div>' +
         '<hr>' +
         '<h2><a href="http://trac.webkit.org/browser/trunk/LayoutTests/bar/nonexistant.html" target="_blank">bar/nonexistant.html</a></h2>' +
         htmlForIndividualTestOnAllBuilders(test2) +
         '<div class=expectations test=bar/nonexistant.html>' +
-            '<div><span class=link onclick=\"setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
-            '<span class=link onclick=\"setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
+            '<div><span class=link onclick=\"g_history.setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
+            '<span class=link onclick=\"g_history.setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
             '<b>Only shows actual results/diffs from the most recent *failure* on each bot.</b></div>' +
         '</div>');
 
     tests = [test1];
     equal(htmlForIndividualTests(tests), htmlForIndividualTestOnAllBuilders(test1) +
         '<div class=expectations test=foo/nonexistant.html>' +
-            '<div><span class=link onclick=\"setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
-            '<span class=link onclick=\"setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
+            '<div><span class=link onclick=\"g_history.setQueryParameter(\'showExpectations\', true)\">Show results</span> | ' +
+            '<span class=link onclick=\"g_history.setQueryParameter(\'showLargeExpectations\', true)\">Show large thumbnails</span> | ' +
             '<b>Only shows actual results/diffs from the most recent *failure* on each bot.</b></div>' +
         '</div>');
 
-    g_currentState.showChrome = true;
+    historyInstance.dashboardSpecificState.showChrome = true;
 
     equal(htmlForIndividualTests(tests),
         '<h2><a href="http://trac.webkit.org/browser/trunk/LayoutTests/foo/nonexistant.html" target="_blank">foo/nonexistant.html</a></h2>' +
@@ -479,16 +514,16 @@
 });
 
 test('htmlForSingleTestRow', 1, function() {
-    resetGlobals();
+    var historyInstance = resetGlobals();
     var builder = 'dummyBuilder';
     BUILDER_TO_MASTER[builder] = CHROMIUM_WEBKIT_BUILDER_MASTER;
     var test = createResultsObjectForTest('foo/exists.html', builder);
-    g_currentState.showCorrectExpectations = true;
+    historyInstance.dashboardSpecificState.showCorrectExpectations = true;
     g_resultsByBuilder[builder] = {buildNumbers: [2, 1], webkitRevision: [1234, 1233]};
     test.rawResults = [[1, 'F'], [2, 'I']];
     test.rawTimes = [[1, 0], [2, 5]];
     var expected = '<tr>' +
-        '<td class="test-link"><span class="link" onclick="setQueryParameter(\'tests\',\'foo/exists.html\');">foo/exists.html</span>' +
+        '<td class="test-link"><span class="link" onclick="g_history.setQueryParameter(\'tests\',\'foo/exists.html\');">foo/exists.html</span>' +
         '<td class=options-container><a href="https://bugs.webkit.org/enter_bug.cgi?assigned_to=webkit-unassigned%40lists.webkit.org&product=WebKit&form_name=enter_bug&component=Tools%20%2F%20Tests&short_desc=Layout%20Test%20foo%2Fexists.html%20is%20failing&comment=The%20following%20layout%20test%20is%20failing%20on%20%5Binsert%20platform%5D%0A%0Afoo%2Fexists.html%0A%0AProbable%20cause%3A%0A%0A%5Binsert%20probable%20cause%5D" class="file-bug">FILE BUG</a>' +
         '<td class=options-container>' +
             '<td class=options-container>' +
@@ -509,191 +544,46 @@
 });
 
 // FIXME: Create builders_tests.js and move this there.
-test('generateChromiumDepsFyiGpuBuildersFromBuilderList', 1, function() {
-    var builderList = ["Linux Audio", "Linux Release (ATI)", "Linux Release (Intel)", "Mac Release (ATI)", "Win7 Audio", "Win7 Release (ATI)", "Win7 Release (Intel)", "WinXP Debug (NVIDIA)", "WinXP Release (NVIDIA)"];
-    var expectedBuilders = [["Linux Release (ATI)", 2], ["Linux Release (Intel)"], ["Mac Release (ATI)"], ["Win7 Release (ATI)"], ["Win7 Release (Intel)"], ["WinXP Debug (NVIDIA)"], ["WinXP Release (NVIDIA)"] ];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumDepsFyiGpuTestRunner), expectedBuilders);
+
+test('isChromiumWebkitTipOfTreeTestRunner', 1, function() {
+    var builderList = ["WebKit Linux", "WebKit Linux (dbg)", "WebKit Linux 32", "WebKit Mac10.6", "WebKit Mac10.6 (dbg)",
+        "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)", "WebKit Win (deps)",
+        "WebKit Win7", "Linux (Content Shell)"];
+    var expectedBuilders = ["WebKit Linux", "WebKit Linux (dbg)", "WebKit Linux 32", "WebKit Mac10.6",
+        "WebKit Mac10.6 (dbg)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)", "WebKit Win7"];
+    deepEqual(builderList.filter(isChromiumWebkitTipOfTreeTestRunner), expectedBuilders);
 });
 
-test('generateChromiumTipOfTreeGpuBuildersFromBuilderList', 1, function() {
-    var builderList = ["Chrome Frame Tests", "GPU Linux (NVIDIA)", "GPU Linux (dbg) (NVIDIA)", "GPU Mac", "GPU Mac (dbg)", "GPU Win7 (NVIDIA)", "GPU Win7 (dbg) (NVIDIA)", "Linux Perf",
-        "Linux Tests", "Linux Valgrind", "Mac Builder (dbg)", "Mac10.6 Perf", "Mac10.6 Tests", "Vista Perf", "Vista Tests", "WebKit Linux", "WebKit Linux ASAN", "WebKit Linux (dbg)", "WebKit Linux (deps)",
-        "WebKit Linux 32", "WebKit Mac Builder", "WebKit Mac Builder (dbg)", "WebKit Mac Builder (deps)",
-        "WebKit Mac10.6", "WebKit Mac10.6 (dbg)", "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)",
-        "WebKit Win (deps)", "WebKit Win Builder", "WebKit Win Builder (dbg)", "WebKit Win Builder (deps)", "WebKit Win7", "Win (dbg)", "Win Builder"];
-    var expectedBuilders = [["GPU Linux (NVIDIA)", 2], ["GPU Linux (dbg) (NVIDIA)"], ["GPU Mac"], ["GPU Mac (dbg)"], ["GPU Win7 (NVIDIA)"], ["GPU Win7 (dbg) (NVIDIA)"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumTipOfTreeGpuTestRunner), expectedBuilders);
-});
-
-test('generateWebkitBuildersFromBuilderList', 1, function() {
-    var builderList = ["Chromium Android Release", "Chromium Linux Release", "Chromium Linux Release (Grid Layout)", "Chromium Linux Release (Perf)", "Chromium Linux Release (Tests)",
-        "Chromium Mac Release", "Chromium Mac Release (Perf)", "Chromium Mac Release (Tests)", "Chromium Win Release", "Chromium Win Release (Perf)", "Chromium Win Release (Tests)",
-        "EFL Linux Release", "GTK Linux 32-bit Release", "GTK Linux 64-bit Debug", "GTK Linux 64-bit Release", "Lion Debug (Build)", "Lion Debug (Tests)", "Lion Debug (WebKit2 Tests)",
-        "Lion Leaks", "Lion Release (Build)", "Lion Release (Perf)", "Lion Release (Tests)", "Lion Release (WebKit2 Tests)", "Qt Linux 64-bit Release (Perf)",
-        "Qt Linux 64-bit Release (WebKit2 Perf)", "Qt Linux ARMv7 Release", "Qt Linux MIPS Release", "Qt Linux Release", "Qt Linux Release minimal", "Qt Linux SH4 Release",
-        "Qt SnowLeopard Release", "Qt Windows 32-bit Debug", "Qt Windows 32-bit Release", "SnowLeopard Intel Debug (Build)", "SnowLeopard Intel Debug (Tests)",
-        "SnowLeopard Intel Debug (WebKit2 Tests)", "SnowLeopard Intel Release (Build)", "SnowLeopard Intel Release (Tests)", "SnowLeopard Intel Release (WebKit2 Tests)",
-        "WinCE Release (Build)", "WinCairo Release", "Windows 7 Release (Tests)", "Windows 7 Release (WebKit2 Tests)", "Windows Debug (Build)", "Windows Release (Build)", "Windows XP Debug (Tests)",
-        "EFL Linux 32-bit Release (Build)", "EFL Linux 64-bit Debug"];
-    var expectedBuilders = [["Chromium Linux Release (Tests)", 2], ["Chromium Mac Release (Tests)"], ["EFL Linux Release"], ["GTK Linux 32-bit Release"], ["GTK Linux 64-bit Debug"],
-        ["GTK Linux 64-bit Release"], ["Lion Debug (Tests)"], ["Lion Debug (WebKit2 Tests)"], ["Lion Release (Tests)"], ["Lion Release (WebKit2 Tests)"], ["Qt Linux Release"],
-        ["SnowLeopard Intel Debug (Tests)"], ["SnowLeopard Intel Debug (WebKit2 Tests)"], ["SnowLeopard Intel Release (Tests)"], ["SnowLeopard Intel Release (WebKit2 Tests)"], ["EFL Linux 64-bit Debug"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isWebkitTestRunner), expectedBuilders);
-});
-
-test('generateChromiumWebkitTipOfTreeBuildersFromBuilderList', 1, function() {
+test('isChromiumWebkitDepsTestRunner', 1, function() {
     var builderList = ["Chrome Frame Tests", "GPU Linux (NVIDIA)", "GPU Linux (dbg) (NVIDIA)", "GPU Mac", "GPU Mac (dbg)", "GPU Win7 (NVIDIA)", "GPU Win7 (dbg) (NVIDIA)", "Linux Perf", "Linux Tests",
         "Linux Valgrind", "Mac Builder (dbg)", "Mac10.6 Perf", "Mac10.6 Tests", "Vista Perf", "Vista Tests", "WebKit Linux", "WebKit Linux ASAN",  "WebKit Linux (dbg)", "WebKit Linux (deps)", "WebKit Linux 32",
-        "WebKit Mac Builder", "WebKit Mac Builder (dbg)", "WebKit Mac Builder (deps)", "WebKit Mac10.6", "WebKit Mac10.6 (dbg)",
-        "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)", "WebKit Win (deps)", "WebKit Win Builder", "WebKit Win Builder (dbg)",
-        "WebKit Win Builder (deps)", "WebKit Win7", "Win (dbg)", "Win Builder",
-        "Linux (Content Shell)"];
-    var expectedBuilders = [["WebKit Linux", 2], ["WebKit Linux (dbg)"], ["WebKit Linux 32"], ["WebKit Mac10.6"],
-        ["WebKit Mac10.6 (dbg)"], ["WebKit Mac10.7"], ["WebKit Win"], ["WebKit Win (dbg)(1)"], ["WebKit Win (dbg)(2)"], ["WebKit Win7"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumWebkitTipOfTreeTestRunner), expectedBuilders);
-});
-
-test('generateChromiumWebkitDepsBuildersFromBuilderList', 1, function() {
-    var builderList = ["Chrome Frame Tests", "GPU Linux (NVIDIA)", "GPU Linux (dbg) (NVIDIA)", "GPU Mac", "GPU Mac (dbg)", "GPU Win7 (NVIDIA)", "GPU Win7 (dbg) (NVIDIA)", "Linux Perf", "Linux Tests",
-        "Linux Valgrind", "Mac Builder (dbg)", "Mac10.6 Perf", "Mac10.6 Tests", "Vista Perf", "Vista Tests", "WebKit Linux", "WebKit Linux ASAN",  "WebKit Linux (dbg)", "WebKit Linux (deps)", "WebKit Linux 32",
-        "WebKit Mac Builder", "WebKit Mac Builder (dbg)", "WebKit Mac Builder (deps)", "WebKit Mac10.6", "WebKit Mac10.6 (dbg)",
-        "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)", "WebKit Win (deps)", "WebKit Win Builder", "WebKit Win Builder (dbg)",
-        "WebKit Win Builder (deps)", "WebKit Win7", "Win (dbg)", "Win Builder"];
-    var expectedBuilders = [["WebKit Linux (deps)", 2], ["WebKit Mac10.6 (deps)"], ["WebKit Win (deps)"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumWebkitDepsTestRunner), expectedBuilders);
-});
-
-test('generateChromiumDepsGTestBuildersFromBuilderList', 1, function() {
-    var builderList = ["Android Builder", "Chrome Frame Tests (ie6)", "Chrome Frame Tests (ie7)", "Chrome Frame Tests (ie8)", "Interactive Tests (dbg)", "Linux", "Linux Builder (dbg)",
-        "Linux Builder (dbg)(shared)", "Linux Builder x64", "Linux Clang (dbg)", "Linux Sync", "Linux Tests (dbg)(1)", "Linux Tests (dbg)(2)", "Linux Tests (dbg)(shared)", "Linux Tests x64",
-        "Linux x64", "Mac", "Mac 10.6 Tests (dbg)(1)", "Mac 10.6 Tests (dbg)(2)",
-        "Mac 10.6 Tests (dbg)(3)", "Mac 10.6 Tests (dbg)(4)", "Mac Builder", "Mac Builder (dbg)", "Mac10.6 Sync",
-        "Mac10.6 Tests (1)", "Mac10.6 Tests (2)", "Mac10.6 Tests (3)", "NACL Tests", "NACL Tests (x64)", "Vista Tests (1)", "Vista Tests (2)", "Vista Tests (3)", "Win", "Win Aura",
-        "Win Builder", "Win Builder (dbg)", "Win Builder 2010 (dbg)", "Win7 Sync", "Win7 Tests (1)", "Win7 Tests (2)", "Win7 Tests (3)", "Win7 Tests (dbg)(1)", "Win7 Tests (dbg)(2)",
-        "Win7 Tests (dbg)(3)", "Win7 Tests (dbg)(4)", "Win7 Tests (dbg)(5)", "Win7 Tests (dbg)(6)", "XP Tests (1)", "XP Tests (2)", "XP Tests (3)", "XP Tests (dbg)(1)", "XP Tests (dbg)(2)",
-        "XP Tests (dbg)(3)", "XP Tests (dbg)(4)", "XP Tests (dbg)(5)", "XP Tests (dbg)(6)"];
-    var expectedBuilders = [["Interactive Tests (dbg)", 2], ["Linux Sync"], ["Linux Tests (dbg)(1)"], ["Linux Tests (dbg)(2)"], ["Linux Tests (dbg)(shared)"], ["Linux Tests x64"],
-        ["Mac 10.6 Tests (dbg)(1)"], ["Mac 10.6 Tests (dbg)(2)"], ["Mac 10.6 Tests (dbg)(3)"],
-        ["Mac 10.6 Tests (dbg)(4)"], ["Mac10.6 Sync"], ["Mac10.6 Tests (1)"], ["Mac10.6 Tests (2)"], ["Mac10.6 Tests (3)"], ["NACL Tests"],
-        ["NACL Tests (x64)"], ["Vista Tests (1)"], ["Vista Tests (2)"], ["Vista Tests (3)"], ["Win7 Sync"], ["Win7 Tests (1)"], ["Win7 Tests (2)"], ["Win7 Tests (3)"], ["Win7 Tests (dbg)(1)"],
-        ["Win7 Tests (dbg)(2)"], ["Win7 Tests (dbg)(3)"], ["Win7 Tests (dbg)(4)"], ["Win7 Tests (dbg)(5)"], ["Win7 Tests (dbg)(6)"], ["XP Tests (1)"], ["XP Tests (2)"], ["XP Tests (3)"],
-        ["XP Tests (dbg)(1)"], ["XP Tests (dbg)(2)"], ["XP Tests (dbg)(3)"], ["XP Tests (dbg)(4)"], ["XP Tests (dbg)(5)"], ["XP Tests (dbg)(6)"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumDepsGTestRunner), expectedBuilders);
-});
-
-test('generateChromiumDepsCrosGTestBuildersFromBuilderList', 1, function() {
-    var builderList = ["ChromiumOS (amd64)", "ChromiumOS (arm)", "ChromiumOS (tegra2)", "ChromiumOS (x86)", "Linux ChromiumOS (Clang dbg)", "Linux ChromiumOS Builder", "Linux ChromiumOS Builder (dbg)",
-        "Linux ChromiumOS Tests (1)", "Linux ChromiumOS Tests (2)", "Linux ChromiumOS Tests (dbg)(1)", "Linux ChromiumOS Tests (dbg)(2)", "Linux ChromiumOS Tests (dbg)(3)"];
-    var expectedBuilders = [["Linux ChromiumOS Tests (1)", 2], ["Linux ChromiumOS Tests (2)"], ["Linux ChromiumOS Tests (dbg)(1)"], ["Linux ChromiumOS Tests (dbg)(2)"], ["Linux ChromiumOS Tests (dbg)(3)"]];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumDepsCrosGTestRunner), expectedBuilders);
-});
-
-test('generateChromiumTipOfTreeGTestBuildersFromBuilderList', 1, function() {
-    var builderList = ["Chrome Frame Tests", "GPU Linux (NVIDIA)", "GPU Linux (dbg) (NVIDIA)", "GPU Mac", "GPU Mac (dbg)", "GPU Win7 (NVIDIA)", "GPU Win7 (dbg) (NVIDIA)", "Linux Perf",
-        "Linux Tests", "Linux Valgrind", "Mac Builder (dbg)", "Mac10.6 Perf", "Mac10.6 Tests", "Vista Perf", "Vista Tests", "WebKit Linux", "WebKit Linux (dbg)", "WebKit Linux (deps)",
-        "WebKit Linux 32", "WebKit Mac Builder", "WebKit Mac Builder (dbg)", "WebKit Mac Builder (deps)",
-        "WebKit Mac10.6", "WebKit Mac10.6 (dbg)", "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)",
-        "WebKit Win (deps)", "WebKit Win Builder", "WebKit Win Builder (dbg)", "WebKit Win Builder (deps)", "WebKit Win7", "Win (dbg)", "Win Builder"];
-    var expectedBuilders = [['Linux Tests', BuilderGroup.DEFAULT_BUILDER], ['Mac10.6 Tests'], ['Vista Tests'], ['Win (dbg)']];
-    deepEqual(generateBuildersFromBuilderList(builderList, isChromiumTipOfTreeGTestRunner), expectedBuilders);
-});
-
-test('queryHashAsMap', 2, function() {
-    equal(window.location.hash, '#useTestData=true');
-    deepEqual(queryHashAsMap(), {useTestData: 'true'});
-});
-
-test('parseCrossDashboardParameters', 2, function() {
-    equal(window.location.hash, '#useTestData=true');
-    parseCrossDashboardParameters();
-
-    var expectedParameters = {};
-    for (var key in g_defaultCrossDashboardStateValues)
-        expectedParameters[key] = g_defaultCrossDashboardStateValues[key];
-    expectedParameters.useTestData = true;
-
-    deepEqual(g_crossDashboardState, expectedParameters);
-});
-
-test('diffStates', 5, function() {
-    var newState = {a: 1, b: 2};
-    deepEqual(diffStates(null, newState), newState);
-
-    var oldState = {a: 1};
-    deepEqual(diffStates(oldState, newState), {b: 2});
-
-    // FIXME: This is kind of weird. I think the existing users of this code work correctly, but it's a confusing result.
-    var oldState = {c: 1};
-    deepEqual(diffStates(oldState, newState), {a:1, b: 2});
-
-    var oldState = {a: 1, b: 2};
-    deepEqual(diffStates(oldState, newState), {});
-
-    var oldState = {a: 2, b: 3};
-    deepEqual(diffStates(oldState, newState), {a: 1, b: 2});
-});
-
-test('addBuilderLoadErrors', 1, function() {
-    clearErrors();
-    g_hasDoneInitialPageGeneration = false;
-    g_buildersThatFailedToLoad = ['builder1', 'builder2'];
-    g_staleBuilders = ['staleBuilder1'];
-    addBuilderLoadErrors();
-    equal(g_errorMessages, 'ERROR: Failed to get data from builder1,builder2.<br>ERROR: Data from staleBuilder1 is more than 1 day stale.<br>');
+        "WebKit Mac10.6", "WebKit Mac10.6 (dbg)", "WebKit Mac10.6 (deps)", "WebKit Mac10.7", "WebKit Win", "WebKit Win (dbg)(1)", "WebKit Win (dbg)(2)", "WebKit Win (deps)",
+        "WebKit Win7", "Win (dbg)", "Win Builder"];
+    var expectedBuilders = ["WebKit Linux (deps)", "WebKit Mac10.6 (deps)", "WebKit Win (deps)"];
+    deepEqual(builderList.filter(isChromiumWebkitDepsTestRunner), expectedBuilders);
 });
 
 test('builderGroupIsToTWebKitAttribute', 2, function() {
-    var dummyMaster = new BuilderMaster('dummy.org', 'http://build.dummy.org');
+    var dummyMaster = new builders.BuilderMaster('Chromium', 'dummyurl', {'layout-tests': {'builders': ['WebKit Linux', 'WebKit Linux (dbg)', 'WebKit Mac10.7', 'WebKit Win']}});
     var testBuilderGroups = {
         '@ToT - dummy.org': new BuilderGroup(BuilderGroup.TOT_WEBKIT),
         '@DEPS - dummy.org': new BuilderGroup(BuilderGroup.DEPS_WEBKIT),
     }
-    testBuilderGroups['@ToT - dummy.org'].expectedGroups = 1;
-    testBuilderGroups['@DEPS - dummy.org'].expectedGroups = 1;
 
     var testJSONData = "{ \"Dummy Builder 1\": null, \"Dummy Builder 2\": null }";
-    onBuilderListLoad(testBuilderGroups, function() { return true; }, dummyMaster, '@ToT - dummy.org', {responseText: testJSONData});
+    requestBuilderList(testBuilderGroups, 'ChromiumWebkit', '@ToT - dummy.org', testBuilderGroups['@ToT - dummy.org'], 'layout-tests');
     equal(testBuilderGroups['@ToT - dummy.org'].isToTWebKit, true);
-    onBuilderListLoad(testBuilderGroups, function() { return true; }, dummyMaster, '@DEPS - dummy.org', {responseText: testJSONData});
+    requestBuilderList(testBuilderGroups, 'ChromiumWebkit', '@DEPS - dummy.org', testBuilderGroups['@DEPS - dummy.org'], 'layout-tests');
     equal(testBuilderGroups['@DEPS - dummy.org'].isToTWebKit, false);
 });
 
-test('builderGroupExpectedGroups', 4, function() {
-    var dummyMaster = new BuilderMaster('dummy.org', 'http://build.dummy.org');
-    var testBuilderGroups = {
-        '@ToT - dummy.org': new BuilderGroup(BuilderGroup.TOT_WEBKIT),
-    }
-    testBuilderGroups['@ToT - dummy.org'].expectedGroups = 3;
-
-    var testJSONData = "{ \"Dummy Builder 1\": null }";
-    equal(testBuilderGroups['@ToT - dummy.org'].expectedGroups, 3);
-    onBuilderListLoad(testBuilderGroups,  function() { return true; }, dummyMaster, '@ToT - dummy.org', {responseText: testJSONData});
-    equal(testBuilderGroups['@ToT - dummy.org'].groups, 1);
-    var testJSONData = "{ \"Dummy Builder 2\": null }";
-    onBuilderListLoad(testBuilderGroups,  function() { return true; }, dummyMaster, '@ToT - dummy.org', {responseText: testJSONData});
-    equal(testBuilderGroups['@ToT - dummy.org'].groups, 2);
-    onErrorLoadingBuilderList('http://build.dummy.org', testBuilderGroups,  '@ToT - dummy.org');
-    equal(testBuilderGroups['@ToT - dummy.org'].groups, 3);
-});
-
-test('requestBuilderListAddsBuilderGroupEntry', 2, function() {
+test('requestBuilderListAddsBuilderGroupEntry', 1, function() {
     var testBuilderGroups = { '@ToT - dummy.org': null };
+    var builderGroup = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
+    var groupName = '@ToT - dummy.org';
+    requestBuilderList(testBuilderGroups, 'ChromiumWebkit', groupName, builderGroup, 'layout-tests');
 
-    var requestFunction = loader.request;
-    loader.request = function() {};
-
-    try {
-        var builderFilter = null;
-        var master = { builderJsonPath: function() {} };
-        var groupName = '@ToT - dummy.org';
-        var builderGroup = { expectedGroups: 0 };
-        requestBuilderList(testBuilderGroups, builderFilter, master, groupName, builderGroup);
-
-        equal(testBuilderGroups['@ToT - dummy.org'], builderGroup);
-        equal(testBuilderGroups['@ToT - dummy.org'].expectedGroups, 1);
-    } finally {
-        loader.request = requestFunction;
-    }
+    equal(testBuilderGroups['@ToT - dummy.org'], builderGroup);
 })
 
 test('sortTests', 4, function() {
@@ -722,9 +612,9 @@
 });
 
 test('popup', 2, function() {
-    showPopup(document.body, 'dummy content');
+    ui.popup.show(document.body, 'dummy content');
     ok(document.querySelector('#popup'));
-    hidePopup();
+    ui.popup.hide();
     ok(!document.querySelector('#popup'));
 });
 
@@ -797,3 +687,15 @@
     }, "foo/bar");
     deepEqual(leafsOfPartialTrieTraversal, expectedLeafs);
 });
+
+test('changeTestTypeInvalidatesGroup', 1, function() {
+    var historyInstance = resetGlobals();
+    var originalGroup = '@ToT - chromium.org';
+    var originalTestType = 'layout-tests';
+    loadBuildersList(originalGroup, originalTestType);
+    historyInstance.crossDashboardState.group = originalGroup;
+    historyInstance.crossDashboardState.testType = originalTestType;
+
+    historyInstance.invalidateQueryParameters({'testType': 'ui_tests'});
+    notEqual(historyInstance.crossDashboardState.group, originalGroup, "group should have been invalidated");   
+});
\ No newline at end of file
diff --git a/Tools/TestResultServer/static-dashboards/history.js b/Tools/TestResultServer/static-dashboards/history.js
new file mode 100644
index 0000000..49c7108
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/history.js
@@ -0,0 +1,274 @@
+// Copyright (C) 2013 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.
+
+
+var history = history || {};
+
+(function() {
+
+history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES = {
+    group: null,
+    showAllRuns: false,
+    testType: 'layout-tests',
+    useTestData: false,
+}    
+
+history.validateParameter = function(state, key, value, validateFn)
+{
+    if (validateFn())
+        state[key] = value;
+    else
+        console.log(key + ' value is not valid: ' + value);
+}
+
+history.isTreeMap = function()
+{
+    return string.endsWith(window.location.pathname, 'treemap.html');
+}
+
+// TODO(jparent): Make private once callers move here.
+history.queryHashAsMap = function()
+{
+    var hash = window.location.hash;
+    var paramsList = hash ? hash.substring(1).split('&') : [];
+    var paramsMap = {};
+    var invalidKeys = [];
+    for (var i = 0; i < paramsList.length; i++) {
+        var thisParam = paramsList[i].split('=');
+        if (thisParam.length != 2) {
+            console.log('Invalid query parameter: ' + paramsList[i]);
+            continue;
+        }
+
+        paramsMap[thisParam[0]] = decodeURIComponent(thisParam[1]);
+    }
+
+    // FIXME: remove support for mapping from the master parameter to the group
+    // one once the waterfall starts to pass in the builder name instead.
+    if (paramsMap.master) {
+        paramsMap.group = LEGACY_BUILDER_MASTERS_TO_GROUPS[paramsMap.master];
+        if (!paramsMap.group)
+            console.log('ERROR: Unknown master name: ' + paramsMap.master);
+        window.location.hash = window.location.hash.replace('master=' + paramsMap.master, 'group=' + paramsMap.group);
+        delete paramsMap.master;
+    }
+
+    return paramsMap;
+}
+
+history._diffStates = function(oldState, newState)
+{
+    // If there is no old state, everything in the current state is new.
+    if (!oldState)
+        return newState;
+
+    var changedParams = {};
+    for (curKey in newState) {
+        var oldVal = oldState[curKey];
+        var newVal = newState[curKey];
+        // Add new keys or changed values.
+        if (!oldVal || oldVal != newVal)
+            changedParams[curKey] = newVal;
+    }
+    return changedParams;
+}
+
+history._fillMissingValues = function(to, from)
+{
+    for (var state in from) {
+        if (!(state in to))
+            to[state] = from[state];
+    }
+}
+
+history.History = function()
+{
+  this.crossDashboardState = {};
+  this.dashboardSpecificState = {};
+}
+
+var RELOAD_REQUIRING_PARAMETERS = ['showAllRuns', 'group', 'testType'];
+
+history.History.prototype = {
+    isLayoutTestResults: function()
+    {
+        return this.crossDashboardState.testType == 'layout-tests';
+    },
+    isGPUTestResults: function()
+    {
+        return this.crossDashboardState.testType == 'gpu_tests';
+    },
+    parseCrossDashboardParameters: function()
+    {
+        this.crossDashboardState = {};
+        var parameters = history.queryHashAsMap();
+        for (parameterName in history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES)
+            this.parseParameter(parameters, parameterName);
+
+        history._fillMissingValues(this.crossDashboardState, history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES);
+    },
+    // TODO(jparent): Make private once callers move here.
+    parseParameters: function()
+    {
+        var oldCrossDashboardState = this.crossDashboardState;
+        var oldDashboardSpecificState = this.dashboardSpecificState;
+
+        this.parseCrossDashboardParameters();
+        
+        // Some parameters require loading different JSON files when the value changes. Do a reload.
+        if (Object.keys(oldCrossDashboardState).length) {
+            for (var key in this.crossDashboardState) {
+                if (oldCrossDashboardState[key] != this.crossDashboardState[key] && RELOAD_REQUIRING_PARAMETERS.indexOf(key) != -1) {
+                    window.location.reload();
+                    return false;
+                }
+            }
+        }
+
+        parseDashboardSpecificParameters();
+        var dashboardSpecificDiffState = history._diffStates(oldDashboardSpecificState, this.dashboardSpecificState);
+
+        history._fillMissingValues(this.dashboardSpecificState, g_defaultDashboardSpecificStateValues);
+
+        // FIXME: dashboard_base shouldn't know anything about specific dashboard specific keys.
+        if (dashboardSpecificDiffState.builder)
+            delete this.dashboardSpecificState.tests;
+        if (this.dashboardSpecificState.tests)
+            delete this.dashboardSpecificState.builder;
+
+        var shouldGeneratePage = true;
+        if (Object.keys(dashboardSpecificDiffState).length)
+            shouldGeneratePage = handleQueryParameterChange(dashboardSpecificDiffState);
+        return shouldGeneratePage;
+    },
+    // TODO(jparent): Make private once callers move here.
+    parseParameter: function(parameters, key)
+    {
+        if (!(key in parameters))
+            return;
+        var value = parameters[key];
+        if (!this._handleValidHashParameterWrapper(key, value))
+            console.log("Invalid query parameter: " + key + '=' + value);
+    },
+    // Takes a key and a value and sets the this.dashboardSpecificState[key] = value iff key is
+    // a valid hash parameter and the value is a valid value for that key. Handles
+    // cross-dashboard parameters then falls back to calling
+    // handleValidHashParameter for dashboard-specific parameters.
+    //
+    // @return {boolean} Whether the key what inserted into the this.dashboardSpecificState.
+    _handleValidHashParameterWrapper: function(key, value)
+    {
+        switch(key) {
+        case 'testType':
+            history.validateParameter(this.crossDashboardState, key, value,
+                function() { return TEST_TYPES.indexOf(value) != -1; });
+            return true;
+
+        case 'group':
+            history.validateParameter(this.crossDashboardState, key, value,
+                function() {
+                  return value in LAYOUT_TESTS_BUILDER_GROUPS ||
+                      value in CHROMIUM_GPU_TESTS_BUILDER_GROUPS ||
+                      value in CHROMIUM_INSTRUMENTATION_TESTS_BUILDER_GROUPS ||
+                      value in CHROMIUM_GTESTS_BUILDER_GROUPS;
+                });
+            return true;
+
+        case 'useTestData':
+        case 'showAllRuns':
+            this.crossDashboardState[key] = value == 'true';
+            return true;
+
+        default:
+            return handleValidHashParameter(key, value);
+        }
+    },
+    queryParameterValue: function(parameter)
+    {
+        return this.dashboardSpecificState[parameter] || this.crossDashboardState[parameter];
+    }, 
+    // Sets the page state. Takes varargs of key, value pairs.
+    setQueryParameter: function(var_args)
+    {
+        var queryParamsAsState = {};
+        for (var i = 0; i < arguments.length; i += 2) {
+            var key = arguments[i];
+            queryParamsAsState[key] = arguments[i + 1];
+        }
+
+        this.invalidateQueryParameters(queryParamsAsState);
+
+        var newState = this._combinedDashboardState();
+        for (var key in queryParamsAsState) {
+            newState[key] = queryParamsAsState[key];
+        }
+
+        // Note: We use window.location.hash rather that window.location.replace
+        // because of bugs in Chrome where extra entries were getting created
+        // when back button was pressed and full page navigation was occuring.
+        // FIXME: file those bugs.
+        window.location.hash = this._permaLinkURLHash(newState);
+    },
+    toggleQueryParameter: function(param)
+    {
+        this.setQueryParameter(param, !this.queryParameterValue(param));
+    },
+    invalidateQueryParameters: function(queryParamsAsState)
+    {
+        for (var key in queryParamsAsState) {
+            if (key in CROSS_DB_INVALIDATING_PARAMETERS)
+                delete this.crossDashboardState[CROSS_DB_INVALIDATING_PARAMETERS[key]];
+            if (DB_SPECIFIC_INVALIDATING_PARAMETERS && key in DB_SPECIFIC_INVALIDATING_PARAMETERS)
+                delete this.dashboardSpecificState[DB_SPECIFIC_INVALIDATING_PARAMETERS[key]];
+        }
+    },
+    _joinParameters: function(stateObject)
+    {
+        var state = [];
+        for (var key in stateObject) {
+            var value = stateObject[key];
+            if (value != defaultValue(key))
+                state.push(key + '=' + encodeURIComponent(value));
+        }
+        return state.join('&');
+    }, 
+    _permaLinkURLHash: function(opt_state)
+    {
+        var state = opt_state || this._combinedDashboardState();
+        return '#' + this._joinParameters(state);
+    },
+    _combinedDashboardState: function()
+    {
+        var combinedState = Object.create(this.dashboardSpecificState);
+        for (var key in this.crossDashboardState)
+            combinedState[key] = this.crossDashboardState[key];
+        return combinedState;    
+    }
+}
+
+})();
\ No newline at end of file
diff --git a/Tools/TestResultServer/static-dashboards/history_unittests.js b/Tools/TestResultServer/static-dashboards/history_unittests.js
new file mode 100644
index 0000000..7559453
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/history_unittests.js
@@ -0,0 +1,65 @@
+// Copyright (C) 2013 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.
+
+test('queryHashAsMap', 2, function() {
+    equal(window.location.hash, '#useTestData=true');
+    deepEqual(history.queryHashAsMap(), {useTestData: 'true'});
+});
+
+test('diffStates', 5, function() {
+    var newState = {a: 1, b: 2};
+    deepEqual(history._diffStates(null, newState), newState);
+
+    var oldState = {a: 1};
+    deepEqual(history._diffStates(oldState, newState), {b: 2});
+
+    // FIXME: This is kind of weird. I think the existing users of this code work correctly, but it's a confusing result.
+    var oldState = {c: 1};
+    deepEqual(history._diffStates(oldState, newState), {a:1, b: 2});
+
+    var oldState = {a: 1, b: 2};
+    deepEqual(history._diffStates(oldState, newState), {});
+
+    var oldState = {a: 2, b: 3};
+    deepEqual(history._diffStates(oldState, newState), {a: 1, b: 2});
+});
+
+test('parseCrossDashboardParameters', 2, function() {
+    var historyInstance = new history.History();
+    // FIXME(jparent): Remove this once parseParameters moves onto history obj.
+    g_history = historyInstance;
+    equal(window.location.hash, '#useTestData=true');
+    historyInstance.parseCrossDashboardParameters();
+
+    var expectedParameters = {};
+    for (var key in history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES)
+        expectedParameters[key] = history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES[key];
+    expectedParameters.useTestData = true;
+
+    deepEqual(historyInstance.crossDashboardState, expectedParameters);
+});
diff --git a/Tools/TestResultServer/static-dashboards/loader.js b/Tools/TestResultServer/static-dashboards/loader.js
index be9e708..a526eeb 100644
--- a/Tools/TestResultServer/static-dashboards/loader.js
+++ b/Tools/TestResultServer/static-dashboards/loader.js
@@ -37,7 +37,7 @@
 function pathToBuilderResultsFile(builderName) {
     return TEST_RESULTS_SERVER + 'testfile?builder=' + builderName +
            '&master=' + builderMaster(builderName).name +
-           '&testtype=' + g_crossDashboardState.testType + '&name=';
+           '&testtype=' + g_history.crossDashboardState.testType + '&name=';
 }
 
 loader.request = function(url, success, error, opt_isBinaryData)
@@ -57,13 +57,40 @@
     xhr.send();
 }
 
-loader.Loader = function()
+loader.Loader = function(opt_onLoadingComplete)
 {
     this._loadingSteps = [
         this._loadBuildersList,
         this._loadResultsFiles,
         this._loadExpectationsFiles,
     ];
+
+    this._buildersThatFailedToLoad = [];
+    this._staleBuilders = [];
+    this._errors = new ui.Errors();
+    this._onLoadingComplete = opt_onLoadingComplete || function() {};
+    // TODO(jparent): Pass in the appropriate history obj per db.
+    this._history = g_history;
+}
+
+// TODO(aboxhall): figure out whether this is a performance bottleneck and
+// change calling code to understand the trie structure instead if necessary.
+loader.Loader._flattenTrie = function(trie, prefix)
+{
+    var result = {};
+    for (var name in trie) {
+        var fullName = prefix ? prefix + "/" + name : name;
+        var data = trie[name];
+        if ("results" in data)
+            result[fullName] = data;
+        else {
+            var partialResult = loader.Loader._flattenTrie(data, fullName);
+            for (var key in partialResult) {
+                result[key] = partialResult[key];
+            }
+        }
+    }
+    return result;
 }
 
 loader.Loader.prototype = {
@@ -71,37 +98,36 @@
     {
         this._loadNext();
     },
-    buildersListLoaded: function()
+    showErrors: function() 
     {
-        initBuilders();
-        this._loadNext();
+        this._errors.show();
     },
     _loadNext: function()
     {
         var loadingStep = this._loadingSteps.shift();
         if (!loadingStep) {
-            resourceLoadingComplete();
+            this._addErrors();
+            this._onLoadingComplete();
             return;
         }
         loadingStep.apply(this);
     },
     _loadBuildersList: function()
     {
-        loadBuildersList(g_crossDashboardState.group, g_crossDashboardState.testType);
+        loadBuildersList(currentBuilderGroupName(), this._history.crossDashboardState.testType);
+        this._loadNext();
     },
     _loadResultsFiles: function()
     {
-        parseParameters();
-
-        for (var builderName in g_builders)
+        for (var builderName in currentBuilders())
             this._loadResultsFileForBuilder(builderName);
     },
     _loadResultsFileForBuilder: function(builderName)
     {
         var resultsFilename;
-        if (isTreeMap())
+        if (history.isTreeMap())
             resultsFilename = 'times_ms.json';
-        else if (g_crossDashboardState.showAllRuns)
+        else if (this._history.crossDashboardState.showAllRuns)
             resultsFilename = 'results.json';
         else
             resultsFilename = 'results-small.json';
@@ -117,7 +143,7 @@
     },
     _handleResultsFileLoaded: function(builderName, fileData)
     {
-        if (isTreeMap())
+        if (history.isTreeMap())
             this._processTimesJSONData(builderName, fileData);
         else
             this._processResultsJSONData(builderName, fileData);
@@ -154,44 +180,23 @@
                 continue;
 
             if ((Date.now() / 1000) - lastRunSeconds > ONE_DAY_SECONDS)
-                g_staleBuilders.push(builderName);
+                this._staleBuilders.push(builderName);
 
             if (json_version >= 4)
-                builds[builderName][TESTS_KEY] = flattenTrie(builds[builderName][TESTS_KEY]);
+                builds[builderName][TESTS_KEY] = loader.Loader._flattenTrie(builds[builderName][TESTS_KEY]);
             g_resultsByBuilder[builderName] = builds[builderName];
         }
     },
     _handleResultsFileLoadError: function(builderName)
     {
-        var error = 'Failed to load results file for ' + builderName + '.';
+        console.error('Failed to load results file for ' + builderName + '.');
 
-        if (isLayoutTestResults()) {
-            console.error(error);
-            g_buildersThatFailedToLoad.push(builderName);
-        } else {
-            // Avoid to show error/warning messages for non-layout tests. We may be
-            // checking the builders that are not running the tests.
-            console.info('info:' + error);
-        }
+        // FIXME: loader shouldn't depend on state defined in dashboard_base.js.
+        this._buildersThatFailedToLoad.push(builderName);
 
         // Remove this builder from builders, so we don't try to use the
         // data that isn't there.
-        delete g_builders[builderName];
-
-        // Change the default builder name if it has been deleted.
-        if (g_defaultBuilderName == builderName) {
-            g_defaultBuilderName = null;
-            for (var availableBuilderName in g_builders) {
-                g_defaultBuilderName = availableBuilderName;
-                g_defaultDashboardSpecificStateValues.builder = availableBuilderName;
-                break;
-            }
-            if (!g_defaultBuilderName) {
-                var error = 'No tests results found for ' + g_crossDashboardState.testType + '. Reload the page to try fetching it again.';
-                console.error(error);
-                addError(error);
-            }
-        }
+        delete currentBuilders()[builderName];
 
         // Proceed as if the resource had loaded.
         this._handleResourceLoad();
@@ -203,7 +208,7 @@
     },
     _haveResultsFilesLoaded: function()
     {
-        for (var builder in g_builders) {
+        for (var builder in currentBuilders()) {
             if (!g_resultsByBuilder[builder])
                 return false;
         }
@@ -211,7 +216,7 @@
     },
     _loadExpectationsFiles: function()
     {
-        if (!isFlakinessDashboard() && !g_crossDashboardState.useTestData) {
+        if (!isFlakinessDashboard() && !this._history.crossDashboardState.useTestData) {
             this._loadNext();
             return;
         }
@@ -241,6 +246,14 @@
                     partial(function(platformName, xhr) {
                         console.error('Could not load expectations file for ' + platformName);
                     }, platformWithExpectations));
+    },
+    _addErrors: function()
+    {
+        if (this._buildersThatFailedToLoad.length)
+            this._errors.addError('ERROR: Failed to get data from ' + this._buildersThatFailedToLoad.toString() +'.');
+
+        if (this._staleBuilders.length)
+            this._errors.addError('ERROR: Data from ' + this._staleBuilders.toString() + ' is more than 1 day stale.');
     }
 }
 
diff --git a/Tools/TestResultServer/static-dashboards/loader_unittests.js b/Tools/TestResultServer/static-dashboards/loader_unittests.js
index e2f546c..23eab31 100644
--- a/Tools/TestResultServer/static-dashboards/loader_unittests.js
+++ b/Tools/TestResultServer/static-dashboards/loader_unittests.js
@@ -29,8 +29,13 @@
 module('loader');
 
 test('loading steps', 1, function() {
+    resetGlobals();
     var loadedSteps = [];
-    var resourceLoader = new loader.Loader();
+    var loadingCompleteCallback = handleLocationChange;
+    handleLocationChange = function() {
+        deepEqual(loadedSteps, ['step 1', 'step 2']);
+    }
+    var resourceLoader = new loader.Loader(handleLocationChange);
     function loadingStep1() {
         loadedSteps.push('step 1');
         resourceLoader.load();
@@ -40,21 +45,19 @@
         resourceLoader.load();
     }
 
-    var loadingCompleteCallback = resourceLoadingComplete;
-    resourceLoadingComplete = function() {
-        deepEqual(loadedSteps, ['step 1', 'step 2']);
-    }
-
     try {
         resourceLoader._loadingSteps = [loadingStep1, loadingStep2];
         resourceLoader.load();
     } finally {
-        resourceLoadingComplete = loadingCompleteCallback;
+        handleLocationChange = loadingCompleteCallback;
     }
 });
 
-test('results files loading', 5, function() {
-    var expectedLoadedBuilders = ["WebKit Linux", "WebKit Win"];
+// Total number of assertions is 1 for the deepEqual of the builder lists
+// and then 2 per builder (one for ok, one for deepEqual of tests).
+test('results files loading', 11, function() {
+    resetGlobals();
+    var expectedLoadedBuilders =  ['WebKit Linux', 'WebKit Linux (dbg)', 'WebKit Mac10.7', 'WebKit Win', 'WebKit Win (dbg)'];
     var loadedBuilders = [];
     var resourceLoader = new loader.Loader();
     resourceLoader._loadNext = function() {
@@ -67,23 +70,23 @@
 
     var requestFunction = loader.request;
     loader.request = function(url, successCallback, errorCallback) {
-        var builderName = /builder=([\w ]+)&/.exec(url)[1];
+        var builderName = /builder=([\w ().]+)&/.exec(url)[1];
         loadedBuilders.push(builderName);
         successCallback({responseText: '{"version": 4, "' + builderName + '": {"secondsSinceEpoch": [' + Date.now() + '], "tests": {}}}'});
     }
 
-    g_builders = {"WebKit Linux": true, "WebKit Win": true};
-
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+ 
     try {
         resourceLoader._loadResultsFiles();
     } finally {
-        g_builders = undefined;
-        g_resultsByBuilder = undefined;
         loader.request = requestFunction;
     }
 });
 
 test('expectations files loading', 1, function() {
+    resetGlobals();
+    g_history.parseCrossDashboardParameters();
     var expectedLoadedPlatforms = ["chromium", "chromium-android", "efl", "efl-wk1", "efl-wk2", "gtk",
                                    "gtk-wk2", "mac", "mac-lion", "mac-snowleopard", "qt", "win", "wk2"];
     var loadedPlatforms = [];
@@ -104,3 +107,68 @@
         loader.request = requestFunction;
     }
 });
+
+test('results file failing to load', 2, function() {
+    resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+    
+    var resourceLoader = new loader.Loader();
+    var resourceLoadCount = 0;
+    resourceLoader._handleResourceLoad = function() {
+        resourceLoadCount++;
+    }
+
+    var builder1 = 'builder1';
+    currentBuilders()[builder1] = true;
+    resourceLoader._handleResultsFileLoadError(builder1);
+
+    var builder2 = 'builder2';
+    currentBuilders()[builder2] = true;
+    resourceLoader._handleResultsFileLoadError(builder2);
+
+    deepEqual(resourceLoader._buildersThatFailedToLoad, [builder1, builder2]);
+    equal(resourceLoadCount, 2);
+
+});
+
+test('Default builder gets set.', 3, function() {
+    resetGlobals();
+    loadBuildersList('@ToT - chromium.org', 'layout-tests');
+    
+    var defaultBuilder = currentBuilderGroup().defaultBuilder();
+    ok(defaultBuilder, "Default builder should exist.");
+   
+    // Simulate error loading the default builder data, then make sure
+    // a new defaultBuilder is set, and isn't the now invalid one.
+    var resourceLoader = new loader.Loader();
+    resourceLoader._handleResultsFileLoadError(defaultBuilder);
+    var newDefaultBuilder = currentBuilderGroup().defaultBuilder();
+    ok(newDefaultBuilder, "There should still be a default builder.");
+    notEqual(newDefaultBuilder, defaultBuilder, "Default builder should not be the old default builder");
+});
+
+test('addBuilderLoadErrors', 1, function() {
+    var resourceLoader = new loader.Loader();
+    resourceLoader._buildersThatFailedToLoad = ['builder1', 'builder2'];
+    resourceLoader._staleBuilders = ['staleBuilder1'];
+    resourceLoader._addErrors();
+    equal(resourceLoader._errors._messages, 'ERROR: Failed to get data from builder1,builder2.<br>ERROR: Data from staleBuilder1 is more than 1 day stale.<br>');
+});
+
+
+test('flattenTrie', 1, function() {
+    resetGlobals();
+    var tests = {
+        'bar.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
+        'foo': {
+            'bar': {
+                'baz.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
+            }
+        }
+    };
+    var expectedFlattenedTests = {
+        'bar.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
+        'foo/bar/baz.html': {'results': [[100, 'F']], 'times': [[100, 0]]},
+    };
+    equal(JSON.stringify(loader.Loader._flattenTrie(tests)), JSON.stringify(expectedFlattenedTests))
+});
diff --git a/Tools/TestResultServer/static-dashboards/run-embedded-unittests.html b/Tools/TestResultServer/static-dashboards/run-embedded-unittests.html
index 835be21..161ecbb 100644
--- a/Tools/TestResultServer/static-dashboards/run-embedded-unittests.html
+++ b/Tools/TestResultServer/static-dashboards/run-embedded-unittests.html
@@ -40,28 +40,33 @@
 <link rel="stylesheet" href="flakiness_dashboard.css"></link>
 <link rel="stylesheet" href="flakiness_dashboard_tests.css"></link>
 <script src="builders.js"></script>
+<script src="builders_unittests.js"></script>
 
 <script>
-// Don't request the actual builders off the bots when running unittests.
-function loadBuildersList() {};
-function g_handleBuildersListLoaded() {};
-
 // Mimic being embedded. All our embedded checks compare window and parent.
 window.parent = null;
 </script>
 
+<script src="string.js"></script>
+<script src="history.js"></script>
+<script src="history_unittests.js"></script>
 <script src="dashboard_base.js"></script>
+<script src="ui.js"></script>
+<script src="loader.js"></script>
+<script src="loader_unittests.js"></script>
+<script>
+  // Mock out loader.request so that we don't actually make xhrs for tests.
+  loader.request = function(url, successCallback, errorCallback) {};
+</script>
 <script src="flakiness_dashboard.js"></script>
 
 <script>
 window.location.href = '#useTestData=true';
-var builderGroup = '@ToT - chromium.org';
-var builders = {'Webkit Linux': '', 'Webkit Linux (dbg)': '', 'Webkit Mac10.7': '', 'Webkit Win': ''};
-onBuilderListLoad(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumWebkitTipOfTreeTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, builderGroup, BuilderGroup.TOT_WEBKIT, builders);
-initBuilders();
 </script>
 
-<!-- FIXME: Split this up into multiple unittest.js, e.g. one for builders.js and one for dashboard_base.js. -->
 <script src="flakiness_dashboard_embedded_unittests.js"></script>
+<!-- FIXME: Split this up into multiple unittest.js, e.g. one for builders.js and one for dashboard_base.js. -->
+<script src="flakiness_dashboard_unittests.js"></script>
+
 </body>
 </html>
diff --git a/Tools/TestResultServer/static-dashboards/run-unittests.html b/Tools/TestResultServer/static-dashboards/run-unittests.html
index 9999c71..d33c655 100644
--- a/Tools/TestResultServer/static-dashboards/run-unittests.html
+++ b/Tools/TestResultServer/static-dashboards/run-unittests.html
@@ -40,28 +40,25 @@
 <link rel="stylesheet" href="flakiness_dashboard.css"></link>
 <link rel="stylesheet" href="flakiness_dashboard_tests.css"></link>
 <script src="builders.js"></script>
-
-<script>
-// Don't request the actual builders off the bots when running unittests.
-function loadBuildersList() {};
-</script>
-
+<script src="builders_unittests.js"></script>
+<script src="string.js"></script>
+<script src="history.js"></script>
+<script src="history_unittests.js"></script>
 <script src="dashboard_base.js"></script>
+<script src="ui.js"></script>
 <script src="loader.js"></script>
+<script src="loader_unittests.js"></script>
+<script>
+  // Mock out loader.request so that we don't actually make xhrs for tests.
+  loader.request = function(url, successCallback, errorCallback) {};
+</script>
 <script src="flakiness_dashboard.js"></script>
 
 <script>
 window.location.href = '#useTestData=true';
-var groupName = '@ToT - chromium.org';
-var builders = '{"WebKit Linux": true, "WebKit Linux (dbg)": true, "WebKit Mac10.7": true, "WebKit Win": true}';
-LAYOUT_TESTS_BUILDER_GROUPS[groupName] = new BuilderGroup(BuilderGroup.TOT_WEBKIT);
-LAYOUT_TESTS_BUILDER_GROUPS[groupName].expectedGroups = 4;
-onBuilderListLoad(LAYOUT_TESTS_BUILDER_GROUPS, isChromiumWebkitTipOfTreeTestRunner, CHROMIUM_WEBKIT_BUILDER_MASTER, groupName, {responseText: builders});
-initBuilders();
 </script>
 
 <!-- FIXME: Split this up into multiple unittest.js, e.g. one for builders.js and one for dashboard_base.js. -->
 <script src="flakiness_dashboard_unittests.js"></script>
-<script src="loader_unittests.js"></script>
 </body>
 </html>
diff --git a/Tools/TestResultServer/static-dashboards/string.js b/Tools/TestResultServer/static-dashboards/string.js
new file mode 100644
index 0000000..3e1f7f9
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/string.js
@@ -0,0 +1,69 @@
+// Copyright (C) 2013 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.
+
+// @fileoverview Generic string utility functions.
+var string = string || {};
+
+(function() {
+
+string.contains = function(a, b)
+{
+    return a.indexOf(b) != -1;
+}
+
+string.caseInsensitiveContains = function(a, b)
+{
+    return a.match(new RegExp(b, 'i'));
+}
+
+string.startsWith = function(a, b)
+{
+    return a.indexOf(b) == 0;
+}
+
+string.endsWith = function(a, b)
+{
+    return a.lastIndexOf(b) == a.length - b.length;
+}
+
+string.isValidName = function(str)
+{
+    return str.match(/[A-Za-z0-9\-\_,]/);
+}
+
+string.trimString = function(str)
+{
+    return str.replace(/^\s+|\s+$/g, '');
+}
+
+string.collapseWhitespace = function(str)
+{
+    return str.replace(/\s+/g, ' ');
+}
+
+})();
\ No newline at end of file
diff --git a/Tools/TestResultServer/static-dashboards/timeline_explorer.html b/Tools/TestResultServer/static-dashboards/timeline_explorer.html
index b3fa41b..05d2b51 100644
--- a/Tools/TestResultServer/static-dashboards/timeline_explorer.html
+++ b/Tools/TestResultServer/static-dashboards/timeline_explorer.html
@@ -98,355 +98,11 @@
 <script src="dygraph-combined.js"></script>
 <script src="builders.js"></script>
 <script src="loader.js"></script>
+<script src="string.js"></script>
+<script src="history.js"></script>
 <script src="dashboard_base.js"></script>
-<script>
-var FAILING_TESTS_DATASET_NAME = 'Failing tests';
-
-var g_dygraph;
-var g_buildIndicesByTimestamp = {};
-var g_currentBuildIndex = -1;
-var g_currentBuilderTestResults;
-
-//////////////////////////////////////////////////////////////////////////////
-// Methods and objects from dashboard_base.js to override.
-//////////////////////////////////////////////////////////////////////////////
-function generatePage()
-{
-    g_buildIndicesByTimestamp = {};
-    var results = g_resultsByBuilder[g_currentState.builder];
-
-    for (var i = 0; i < results[FIXABLE_COUNTS_KEY].length; i++) {
-        var buildDate = new Date(results[TIMESTAMPS_KEY][i] * 1000);
-        g_buildIndicesByTimestamp[buildDate.getTime()] = i;
-    }
-
-    if (g_currentState.buildTimestamp != -1 && g_currentState.buildTimestamp in g_buildIndicesByTimestamp) {
-        var newBuildIndex = g_buildIndicesByTimestamp[g_currentState.buildTimestamp];
-
-        if (newBuildIndex == g_currentBuildIndex) {
-            // This happens when selectBuild is called, which updates the UI
-            // immediately, in addition to updating the location hash (we don't
-            // just rely on the hash change since we don't want to regenerate the
-            // whole page just because the user clicked on something)
-            return;
-        } else if (newBuildIndex)
-            g_currentBuildIndex = newBuildIndex;
-    }
-
-    initCurrentBuilderTestResults();
-
-    $('test-type-switcher').innerHTML = htmlForTestTypeSwitcher( false,
-        checkboxHTML('ignoreFlakyTests', 'Ignore flaky tests', g_currentState.ignoreFlakyTests, 'g_currentBuildIndex = -1')
-    );
-
-    updateTimelineForBuilder();
-}
-
-function initCurrentBuilderTestResults()
-{
-    var startTime = Date.now();
-    g_currentBuilderTestResults = decompressResults(g_resultsByBuilder[g_currentState.builder]);
-    console.log( 'Time to get test results by build: ' + (Date.now() - startTime));
-}
-
-function handleValidHashParameter(key, value)
-{
-    switch(key) {
-    case 'buildTimestamp':
-        g_currentState.buildTimestamp = parseInt(value, 10);
-        return true;
-    case 'ignoreFlakyTests':
-        g_currentState.ignoreFlakyTests = value == 'true';
-        return true;
-    default:
-        return false;
-    }
-}
-
-g_defaultDashboardSpecificStateValues = {
-    buildTimestamp: -1,
-    ignoreFlakyTests: true
-};
-
-function shouldShowWebKitRevisionsOnly()
-{
-    return isTipOfTreeWebKitBuilder();
-}
-
-function updateTimelineForBuilder()
-{
-    var builder = g_currentState.builder;
-    var results = g_resultsByBuilder[builder];
-    var graphData = [];
-
-    var annotations = [];
-
-    // Dygraph prefers to be handed data in chronological order.
-    for (var i = results[FIXABLE_COUNTS_KEY].length - 1; i >= 0; i--) {
-        var buildDate = new Date(results[TIMESTAMPS_KEY][i] * 1000);
-        // FIXME: Find a better way to exclude outliers. This is just so we
-        // exclude runs where every test failed.
-        var failureCount = Math.min(results[FIXABLE_COUNT_KEY][i], 10000);
-
-        if (g_currentState.ignoreFlakyTests)
-            failureCount -= g_currentBuilderTestResults.flakyDeltasByBuild[i].total || 0;
-
-        graphData.push([buildDate, failureCount]);
-
-        if (!shouldShowWebKitRevisionsOnly() && (results[WEBKIT_REVISIONS_KEY][i] != results[WEBKIT_REVISIONS_KEY][i + 1])) {
-            annotations.push({
-                series: FAILING_TESTS_DATASET_NAME,
-                x: buildDate,
-                shortText: 'R',
-                text: 'WebKit roll: r' + results[WEBKIT_REVISIONS_KEY][i + 1] + ' to ' + results[WEBKIT_REVISIONS_KEY][i]
-            });
-        }
-    }
-
-    var windowWidth = document.documentElement.clientWidth;
-    var windowHeight = document.documentElement.clientHeight;
-    var switcherNode = $('test-type-switcher');
-    var inspectorNode = $('inspector-container');
-    var graphWidth = windowWidth - 20 - inspectorNode.offsetWidth;
-    var graphHeight = windowHeight - switcherNode.offsetTop - switcherNode.offsetHeight - 20;
-
-    var containerNode = $('timeline-container');
-    containerNode.style.height = graphHeight + 'px';
-    containerNode.style.width = graphWidth + 'px';
-    inspectorNode.style.height = graphHeight + 'px';
-
-    g_dygraph = new Dygraph(
-        containerNode,
-        graphData, {
-            labels: ['Date', FAILING_TESTS_DATASET_NAME],
-            width: graphWidth,
-            height: graphHeight,
-            clickCallback: function(event, date) {
-                selectBuild(results, builder, g_dygraph, g_buildIndicesByTimestamp[date]);
-            },
-            drawCallback: function(dygraph, isInitial) {
-                if (isInitial)
-                    return;
-                updateBuildIndicator(results, dygraph);
-            },
-            // xValueParser is necessary for annotations to work, even though we
-            // already have Date instances
-            xValueParser: function(input) { return input.getTime(); }
-        });
-    if (annotations.length)
-        g_dygraph.setAnnotations(annotations);
-
-    inspectorNode.style.visibility = 'visible';
-
-    if (g_currentBuildIndex != -1)
-        selectBuild(results, builder, g_dygraph, g_currentBuildIndex);
-}
-
-function selectBuild(results, builder, dygraph, index)
-{
-    g_currentBuildIndex = index;
-    updateBuildIndicator(results, dygraph);
-    updateBuildInspector(results, builder, dygraph, index);
-    setQueryParameter('buildTimestamp', results[TIMESTAMPS_KEY][index] * 1000);
-}
-
-function updateBuildIndicator(results, dygraph)
-{
-    var indicatorNode = $('indicator');
-
-    if (!indicatorNode) {
-        var containerNode = $('timeline-container');
-        indicatorNode = document.createElement('div');
-        indicatorNode.id = 'indicator';
-        indicatorNode.style.height = containerNode.offsetHeight + 'px';
-        containerNode.appendChild(indicatorNode);
-    }
-
-    if (g_currentBuildIndex == -1)
-        indicatorNode.style.display = 'none';
-    else {
-        indicatorNode.style.display = 'block';
-        var buildDate = new Date(results[TIMESTAMPS_KEY][g_currentBuildIndex] * 1000);
-        var domCoords = dygraph.toDomCoords(buildDate, 0);
-        indicatorNode.style.left = domCoords[0] + 'px';
-    }
-}
-
-function updateBuildInspector(results, builder, dygraph, index)
-{
-    var html = '<table id="inspector-table"><caption>Details</caption>';
-
-    function addRow(label, value)
-    {
-        html += '<tr><td class="label">' + label + '</td><td>' + value + '</td></tr>';
-    }
-
-    // Builder and results links
-    var buildNumber = results[BUILD_NUMBERS_KEY][index];
-    addRow('', '');
-    var master = builderMaster(builder);
-    var buildUrl = master.logPath(builder, results[BUILD_NUMBERS_KEY][index]);
-    if (master == WEBKIT_BUILDER_MASTER) {
-        var resultsUrl = 'http://build.webkit.org/results/' + builder + '/r' + results[WEBKIT_REVISIONS_KEY][index] +
-            ' (' + results[BUILD_NUMBERS_KEY][index] + ')';
-    } else {
-        var resultsUrl = 'http://build.chromium.org/f/chromium/layout_test_results/' +
-            g_builders[builder] + '/' + results[CHROME_REVISIONS_KEY][index];
-    }
-
-    addRow('Build:', '<a href="' + buildUrl + '" target="_blank">' + buildNumber + '</a> (<a href="' + resultsUrl + '" target="_blank">results</a>)');
-
-    // Revision link(s)
-    if (!shouldShowWebKitRevisionsOnly())
-        addRow('Chromium change:', chromiumRevisionLink(results, index));
-    addRow('WebKit change:', webKitRevisionLink(results, index));
-
-    // Test status/counts
-    addRow('', '');
-
-    function addNumberRow(label, currentValue, previousValue)
-    {
-        var delta = currentValue - previousValue;
-        var deltaText = ''
-        if (delta < 0)
-            deltaText = ' <span class="delta negative">' + delta + '</span>';
-        else if (delta > 0)
-            deltaText = ' <span class="delta positive">+' + delta + '</span>';
-
-        addRow(label, currentValue + deltaText);
-    }
-
-    var expectations = expectationsMap();
-    var flakyDeltasByBuild = g_currentBuilderTestResults.flakyDeltasByBuild;
-    for (var expectationKey in expectations) {
-        if (expectationKey in results[FIXABLE_COUNTS_KEY][index]) {
-            var currentCount = results[FIXABLE_COUNTS_KEY][index][expectationKey];
-            var previousCount = results[FIXABLE_COUNTS_KEY][index + 1][expectationKey];
-            if (g_currentState.ignoreFlakyTests) {
-                currentCount -= flakyDeltasByBuild[index][expectationKey] || 0;
-                previousCount -= flakyDeltasByBuild[index + 1][expectationKey] || 0;
-            }
-            addNumberRow(expectations[expectationKey], currentCount, previousCount);
-        }
-    }
-
-    var currentTotal = results[FIXABLE_COUNT_KEY][index];
-    var previousTotal = results[FIXABLE_COUNT_KEY][index + 1];
-    if (g_currentState.ignoreFlakyTests) {
-        currentTotal -= flakyDeltasByBuild[index].total || 0;
-        previousTotal -= flakyDeltasByBuild[index + 1].total || 0;
-    }
-    addNumberRow('Total failing tests:', currentTotal, previousTotal);
-
-    html += '</table>';
-
-    html += '<div id="changes-button" class="buttons">';
-    html += '<button>Show changed test results</button>';
-    html += '</div>';
-
-    html += '<div id="build-buttons" class="buttons">';
-    html += '<button>Previous build</button> <button>Next build</button>';
-    html += '</div>';
-
-    var inspectorNode = $('inspector-container');
-    inspectorNode.innerHTML = html;
-
-    inspectorNode.getElementsByTagName('button')[0].onclick = function() {
-        showResultsDelta(index, buildNumber, buildUrl, resultsUrl);
-    };
-    inspectorNode.getElementsByTagName('button')[1].onclick = function() {
-        selectBuild(results, builder, dygraph, index + 1);
-    };
-    inspectorNode.getElementsByTagName('button')[2].onclick = function() {
-        selectBuild(results, builder, dygraph, index - 1);
-    };
-}
-
-function showResultsDelta(index, buildNumber, buildUrl, resultsUrl)
-{
-    var flakyTests = g_currentBuilderTestResults.flakyTests;
-    var currentResults = g_currentBuilderTestResults.resultsByBuild[index];
-    var testNames = g_currentBuilderTestResults.testNames;
-    var previousResults = g_currentBuilderTestResults.resultsByBuild[index + 1];
-    var expectations = expectationsMap();
-
-    var deltas = {};
-    function addDelta(category, testIndex)
-    {
-        if (g_currentState.ignoreFlakyTests && flakyTests[testIndex])
-            return;
-        if (!(category in deltas))
-            deltas[category] = [];
-        var testName = testNames[testIndex];
-        var flakinessDashboardUrl = 'flakiness_dashboard.html' + (location.hash ? location.hash + '&' : '#') + 'tests=' + testName;
-        var html = '<a href="' + flakinessDashboardUrl + '">' + testName + '</a>';
-        if (flakyTests[testIndex])
-            html += ' <span style="color: #f66">possibly flaky</span>';
-        deltas[category].push(html);
-    }
-
-    for (var testIndex = 0; testIndex < currentResults.length; testIndex++) {
-        if (currentResults[testIndex] === undefined)
-            continue;
-
-        if (previousResults[testIndex] !== undefined) {
-            if (currentResults[testIndex] == previousResults[testIndex])
-                continue;
-            addDelta('Was <b>' + expectations[previousResults[testIndex]] + '</b> now <b>' + expectations[currentResults[testIndex]] + '</b>', testIndex);
-        } else
-            addDelta('Newly <b>' + expectations[currentResults[testIndex]] + '</b>', testIndex);
-    }
-
-    for (var testIndex = 0; testIndex < previousResults.length; testIndex++) {
-        if (previousResults[testIndex] === undefined)
-            continue;
-        if (currentResults[testIndex] === undefined)
-            addDelta('Was <b>' + expectations[previousResults[testIndex]] + '</b>', testIndex);
-    }
-
-    var html = '';
-
-    html += '<head><base target="_blank"></head>';
-    html += '<h1>Changes in test results</h1>';
-
-    html += '<p>For build <a href="' + buildUrl + '" target="_blank">' +
-        buildNumber + '</a> ' + '(<a href="' + resultsUrl +
-        '" target="_blank">results</a>)</p>';
-
-    for (var deltaCategory in deltas) {
-        html += '<p><div>' + deltaCategory + ' (' + deltas[deltaCategory].length + ')</div><ul>';
-        deltas[deltaCategory].forEach(function(deltaHtml) {
-            html += '<li>' + deltaHtml + '</li>';
-        });
-        html += '</ul></p>';
-    }
-
-    var deltaWindow = window.open();
-    deltaWindow.document.write(html);
-}
-
-document.addEventListener('keydown', function(e) {
-    if (g_currentBuildIndex == -1)
-        return;
-
-    switch (e.keyIdentifier) {
-    case 'Left':
-        selectBuild(
-            g_resultsByBuilder[g_currentState.builder],
-            g_currentState.builder,
-            g_dygraph,
-            g_currentBuildIndex + 1);
-        break;
-    case 'Right':
-        selectBuild(
-            g_resultsByBuilder[g_currentState.builder],
-            g_currentState.builder,
-            g_dygraph,
-            g_currentBuildIndex - 1);
-        break;
-    }
-});
-</script>
+<script src="timeline_explorer.js"></script>
+<script src="ui.js"></script>
 </head>
 <body>
     <div id="test-type-switcher"></div>
diff --git a/Tools/TestResultServer/static-dashboards/timeline_explorer.js b/Tools/TestResultServer/static-dashboards/timeline_explorer.js
new file mode 100644
index 0000000..dd498bd
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/timeline_explorer.js
@@ -0,0 +1,493 @@
+// Copyright (C) 2013 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.
+var FAILING_TESTS_DATASET_NAME = 'Failing tests';
+
+var g_dygraph;
+var g_buildIndicesByTimestamp = {};
+var g_currentBuildIndex = -1;
+var g_currentBuilderTestResults;
+
+//////////////////////////////////////////////////////////////////////////////
+// Methods and objects from dashboard_base.js to override.
+//////////////////////////////////////////////////////////////////////////////
+function generatePage()
+{
+    g_buildIndicesByTimestamp = {};
+    var results = g_resultsByBuilder[g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder()];
+
+    for (var i = 0; i < results[FIXABLE_COUNTS_KEY].length; i++) {
+        var buildDate = new Date(results[TIMESTAMPS_KEY][i] * 1000);
+        g_buildIndicesByTimestamp[buildDate.getTime()] = i;
+    }
+
+    if (g_history.dashboardSpecificState.buildTimestamp != -1 && g_history.dashboardSpecificState.buildTimestamp in g_buildIndicesByTimestamp) {
+        var newBuildIndex = g_buildIndicesByTimestamp[g_history.dashboardSpecificState.buildTimestamp];
+
+        if (newBuildIndex == g_currentBuildIndex) {
+            // This happens when selectBuild is called, which updates the UI
+            // immediately, in addition to updating the location hash (we don't
+            // just rely on the hash change since we don't want to regenerate the
+            // whole page just because the user clicked on something)
+            return;
+        } else if (newBuildIndex)
+            g_currentBuildIndex = newBuildIndex;
+    }
+
+    initCurrentBuilderTestResults();
+
+    $('test-type-switcher').innerHTML = ui.html.testTypeSwitcher( false,
+        ui.html.checkbox('ignoreFlakyTests', 'Ignore flaky tests', g_history.dashboardSpecificState.ignoreFlakyTests, 'g_currentBuildIndex = -1')
+    );
+
+    updateTimelineForBuilder();
+}
+
+function initCurrentBuilderTestResults()
+{
+    var startTime = Date.now();
+    g_currentBuilderTestResults = _decompressResults(g_resultsByBuilder[g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder()]);
+    console.log( 'Time to get test results by build: ' + (Date.now() - startTime));
+}
+
+function handleValidHashParameter(key, value)
+{
+    switch(key) {
+    case 'builder':
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
+            function() { return value in currentBuilders(); });
+        return true;
+    case 'buildTimestamp':
+        g_history.dashboardSpecificState.buildTimestamp = parseInt(value, 10);
+        return true;
+    case 'ignoreFlakyTests':
+        g_history.dashboardSpecificState.ignoreFlakyTests = value == 'true';
+        return true;
+    default:
+        return false;
+    }
+}
+
+g_defaultDashboardSpecificStateValues = {
+    builder: null,
+    buildTimestamp: -1,
+    ignoreFlakyTests: true
+};
+
+DB_SPECIFIC_INVALIDATING_PARAMETERS = {
+    'testType': 'builder',
+    'group': 'builder'
+};
+
+function shouldShowWebKitRevisionsOnly()
+{
+    return isTipOfTreeWebKitBuilder();
+}
+
+function updateTimelineForBuilder()
+{
+    var builder = g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder();
+    var results = g_resultsByBuilder[builder];
+    var graphData = [];
+
+    var annotations = [];
+
+    // Dygraph prefers to be handed data in chronological order.
+    for (var i = results[FIXABLE_COUNTS_KEY].length - 1; i >= 0; i--) {
+        var buildDate = new Date(results[TIMESTAMPS_KEY][i] * 1000);
+        // FIXME: Find a better way to exclude outliers. This is just so we
+        // exclude runs where every test failed.
+        var failureCount = Math.min(results[FIXABLE_COUNT_KEY][i], 10000);
+
+        if (g_history.dashboardSpecificState.ignoreFlakyTests)
+            failureCount -= g_currentBuilderTestResults.flakyDeltasByBuild[i].total || 0;
+
+        graphData.push([buildDate, failureCount]);
+
+        if (!shouldShowWebKitRevisionsOnly() && (results[WEBKIT_REVISIONS_KEY][i] != results[WEBKIT_REVISIONS_KEY][i + 1])) {
+            annotations.push({
+                series: FAILING_TESTS_DATASET_NAME,
+                x: buildDate,
+                shortText: 'R',
+                text: 'WebKit roll: r' + results[WEBKIT_REVISIONS_KEY][i + 1] + ' to ' + results[WEBKIT_REVISIONS_KEY][i]
+            });
+        }
+    }
+
+    var windowWidth = document.documentElement.clientWidth;
+    var windowHeight = document.documentElement.clientHeight;
+    var switcherNode = $('test-type-switcher');
+    var inspectorNode = $('inspector-container');
+    var graphWidth = windowWidth - 20 - inspectorNode.offsetWidth;
+    var graphHeight = windowHeight - switcherNode.offsetTop - switcherNode.offsetHeight - 20;
+
+    var containerNode = $('timeline-container');
+    containerNode.style.height = graphHeight + 'px';
+    containerNode.style.width = graphWidth + 'px';
+    inspectorNode.style.height = graphHeight + 'px';
+
+    g_dygraph = new Dygraph(
+        containerNode,
+        graphData, {
+            labels: ['Date', FAILING_TESTS_DATASET_NAME],
+            width: graphWidth,
+            height: graphHeight,
+            clickCallback: function(event, date) {
+                selectBuild(results, builder, g_dygraph, g_buildIndicesByTimestamp[date]);
+            },
+            drawCallback: function(dygraph, isInitial) {
+                if (isInitial)
+                    return;
+                updateBuildIndicator(results, dygraph);
+            },
+            // xValueParser is necessary for annotations to work, even though we
+            // already have Date instances
+            xValueParser: function(input) { return input.getTime(); }
+        });
+    if (annotations.length)
+        g_dygraph.setAnnotations(annotations);
+
+    inspectorNode.style.visibility = 'visible';
+
+    if (g_currentBuildIndex != -1)
+        selectBuild(results, builder, g_dygraph, g_currentBuildIndex);
+}
+
+function selectBuild(results, builder, dygraph, index)
+{
+    g_currentBuildIndex = index;
+    updateBuildIndicator(results, dygraph);
+    updateBuildInspector(results, builder, dygraph, index);
+    g_history.setQueryParameter('buildTimestamp', results[TIMESTAMPS_KEY][index] * 1000);
+}
+
+function updateBuildIndicator(results, dygraph)
+{
+    var indicatorNode = $('indicator');
+
+    if (!indicatorNode) {
+        var containerNode = $('timeline-container');
+        indicatorNode = document.createElement('div');
+        indicatorNode.id = 'indicator';
+        indicatorNode.style.height = containerNode.offsetHeight + 'px';
+        containerNode.appendChild(indicatorNode);
+    }
+
+    if (g_currentBuildIndex == -1)
+        indicatorNode.style.display = 'none';
+    else {
+        indicatorNode.style.display = 'block';
+        var buildDate = new Date(results[TIMESTAMPS_KEY][g_currentBuildIndex] * 1000);
+        var domCoords = dygraph.toDomCoords(buildDate, 0);
+        indicatorNode.style.left = domCoords[0] + 'px';
+    }
+}
+
+function updateBuildInspector(results, builder, dygraph, index)
+{
+    var html = '<table id="inspector-table"><caption>Details</caption>';
+
+    function addRow(label, value)
+    {
+        html += '<tr><td class="label">' + label + '</td><td>' + value + '</td></tr>';
+    }
+
+    // Builder and results links
+    var buildNumber = results[BUILD_NUMBERS_KEY][index];
+    addRow('', '');
+    var master = builderMaster(builder);
+    var buildUrl = master.logPath(builder, results[BUILD_NUMBERS_KEY][index]);
+    if (master.name == WEBKIT_BUILDER_MASTER) {
+        var resultsUrl = 'http://build.webkit.org/results/' + builder + '/r' + results[WEBKIT_REVISIONS_KEY][index] +
+            ' (' + results[BUILD_NUMBERS_KEY][index] + ')';
+    } else {
+        var resultsUrl = 'http://build.chromium.org/f/chromium/layout_test_results/' +
+            currentBuilders()[builder] + '/' + results[CHROME_REVISIONS_KEY][index];
+    }
+
+    addRow('Build:', '<a href="' + buildUrl + '" target="_blank">' + buildNumber + '</a> (<a href="' + resultsUrl + '" target="_blank">results</a>)');
+
+    // Revision link(s)
+    if (!shouldShowWebKitRevisionsOnly())
+        addRow('Chromium change:', ui.html.chromiumRevisionLink(results, index));
+    addRow('WebKit change:', ui.html.webKitRevisionLink(results, index));
+
+    // Test status/counts
+    addRow('', '');
+
+    function addNumberRow(label, currentValue, previousValue)
+    {
+        var delta = currentValue - previousValue;
+        var deltaText = ''
+        if (delta < 0)
+            deltaText = ' <span class="delta negative">' + delta + '</span>';
+        else if (delta > 0)
+            deltaText = ' <span class="delta positive">+' + delta + '</span>';
+
+        addRow(label, currentValue + deltaText);
+    }
+
+    var expectations = expectationsMap();
+    var flakyDeltasByBuild = g_currentBuilderTestResults.flakyDeltasByBuild;
+    for (var expectationKey in expectations) {
+        if (expectationKey in results[FIXABLE_COUNTS_KEY][index]) {
+            var currentCount = results[FIXABLE_COUNTS_KEY][index][expectationKey];
+            var previousCount = results[FIXABLE_COUNTS_KEY][index + 1][expectationKey];
+            if (g_history.dashboardSpecificState.ignoreFlakyTests) {
+                currentCount -= flakyDeltasByBuild[index][expectationKey] || 0;
+                previousCount -= flakyDeltasByBuild[index + 1][expectationKey] || 0;
+            }
+            addNumberRow(expectations[expectationKey], currentCount, previousCount);
+        }
+    }
+
+    var currentTotal = results[FIXABLE_COUNT_KEY][index];
+    var previousTotal = results[FIXABLE_COUNT_KEY][index + 1];
+    if (g_history.dashboardSpecificState.ignoreFlakyTests) {
+        currentTotal -= flakyDeltasByBuild[index].total || 0;
+        previousTotal -= flakyDeltasByBuild[index + 1].total || 0;
+    }
+    addNumberRow('Total failing tests:', currentTotal, previousTotal);
+
+    html += '</table>';
+
+    html += '<div id="changes-button" class="buttons">';
+    html += '<button>Show changed test results</button>';
+    html += '</div>';
+
+    html += '<div id="build-buttons" class="buttons">';
+    html += '<button>Previous build</button> <button>Next build</button>';
+    html += '</div>';
+
+    var inspectorNode = $('inspector-container');
+    inspectorNode.innerHTML = html;
+
+    inspectorNode.getElementsByTagName('button')[0].onclick = function() {
+        showResultsDelta(index, buildNumber, buildUrl, resultsUrl);
+    };
+    inspectorNode.getElementsByTagName('button')[1].onclick = function() {
+        selectBuild(results, builder, dygraph, index + 1);
+    };
+    inspectorNode.getElementsByTagName('button')[2].onclick = function() {
+        selectBuild(results, builder, dygraph, index - 1);
+    };
+}
+
+function showResultsDelta(index, buildNumber, buildUrl, resultsUrl)
+{
+    var flakyTests = g_currentBuilderTestResults.flakyTests;
+    var currentResults = g_currentBuilderTestResults.resultsByBuild[index];
+    var testNames = g_currentBuilderTestResults.testNames;
+    var previousResults = g_currentBuilderTestResults.resultsByBuild[index + 1];
+    var expectations = expectationsMap();
+
+    var deltas = {};
+    function addDelta(category, testIndex)
+    {
+        if (g_history.dashboardSpecificState.ignoreFlakyTests && flakyTests[testIndex])
+            return;
+        if (!(category in deltas))
+            deltas[category] = [];
+        var testName = testNames[testIndex];
+        var flakinessDashboardUrl = 'flakiness_dashboard.html' + (location.hash ? location.hash + '&' : '#') + 'tests=' + testName;
+        var html = '<a href="' + flakinessDashboardUrl + '">' + testName + '</a>';
+        if (flakyTests[testIndex])
+            html += ' <span style="color: #f66">possibly flaky</span>';
+        deltas[category].push(html);
+    }
+
+    for (var testIndex = 0; testIndex < currentResults.length; testIndex++) {
+        if (currentResults[testIndex] === undefined)
+            continue;
+
+        if (previousResults[testIndex] !== undefined) {
+            if (currentResults[testIndex] == previousResults[testIndex])
+                continue;
+            addDelta('Was <b>' + expectations[previousResults[testIndex]] + '</b> now <b>' + expectations[currentResults[testIndex]] + '</b>', testIndex);
+        } else
+            addDelta('Newly <b>' + expectations[currentResults[testIndex]] + '</b>', testIndex);
+    }
+
+    for (var testIndex = 0; testIndex < previousResults.length; testIndex++) {
+        if (previousResults[testIndex] === undefined)
+            continue;
+        if (currentResults[testIndex] === undefined)
+            addDelta('Was <b>' + expectations[previousResults[testIndex]] + '</b>', testIndex);
+    }
+
+    var html = '';
+
+    html += '<head><base target="_blank"></head>';
+    html += '<h1>Changes in test results</h1>';
+
+    html += '<p>For build <a href="' + buildUrl + '" target="_blank">' +
+        buildNumber + '</a> ' + '(<a href="' + resultsUrl +
+        '" target="_blank">results</a>)</p>';
+
+    for (var deltaCategory in deltas) {
+        html += '<p><div>' + deltaCategory + ' (' + deltas[deltaCategory].length + ')</div><ul>';
+        deltas[deltaCategory].forEach(function(deltaHtml) {
+            html += '<li>' + deltaHtml + '</li>';
+        });
+        html += '</ul></p>';
+    }
+
+    var deltaWindow = window.open();
+    deltaWindow.document.write(html);
+}
+
+var _FAILURE_EXPECTATIONS = {
+    'T': 1,
+    'F': 1,
+    'C': 1,
+    'I': 1,
+    'Z': 1
+};
+
+// "Decompresses" the RLE-encoding of test results so that we can query it
+// by build index and test name.
+//
+// @param {Object} results results for the current builder
+// @return Object with these properties:
+//     - testNames: array mapping test index to test names.
+//     - resultsByBuild: array of builds, for each build a (sparse) array of test results by test index.
+//     - flakyTests: array with the boolean value true at test indices that are considered flaky (more than one single-build failure).
+//     - flakyDeltasByBuild: array of builds, for each build a count of flaky test results by expectation, as well as a total.
+function _decompressResults(builderResults)
+{
+    var builderTestResults = builderResults[TESTS_KEY];
+    var buildCount = builderResults[FIXABLE_COUNTS_KEY].length;
+    var resultsByBuild = new Array(buildCount);
+    var flakyDeltasByBuild = new Array(buildCount);
+
+    // Pre-sizing the test result arrays for each build saves us ~250ms
+    var testCount = 0;
+    for (var testName in builderTestResults)
+        testCount++;
+    for (var i = 0; i < buildCount; i++) {
+        resultsByBuild[i] = new Array(testCount);
+        resultsByBuild[i][testCount - 1] = undefined;
+        flakyDeltasByBuild[i] = {};
+    }
+
+    // Using indices instead of the full test names for each build saves us
+    // ~1500ms
+    var testIndex = 0;
+    var testNames = new Array(testCount);
+    var flakyTests = new Array(testCount);
+
+    // Decompress and "invert" test results (by build instead of by test) and
+    // determine which are flaky.
+    for (var testName in builderTestResults) {
+        var oneBuildFailureCount = 0;
+
+        testNames[testIndex] = testName;
+        var testResults = builderTestResults[testName].results;
+        for (var i = 0, rleResult, currentBuildIndex = 0; (rleResult = testResults[i]) && currentBuildIndex < buildCount; i++) {
+            var count = rleResult[RLE.LENGTH];
+            var value = rleResult[RLE.VALUE];
+
+            if (count == 1 && value in _FAILURE_EXPECTATIONS)
+                oneBuildFailureCount++;
+
+            for (var j = 0; j < count; j++) {
+                resultsByBuild[currentBuildIndex++][testIndex] = value;
+                if (currentBuildIndex == buildCount)
+                    break;
+            }
+        }
+
+        if (oneBuildFailureCount > 2)
+            flakyTests[testIndex] = true;
+
+        testIndex++;
+    }
+
+    // Now that we know which tests are flaky, count the test results that are
+    // from flaky tests for each build.
+    testIndex = 0;
+    for (var testName in builderTestResults) {
+        if (!flakyTests[testIndex++])
+            continue;
+
+        var testResults = builderTestResults[testName].results;
+        for (var i = 0, rleResult, currentBuildIndex = 0; (rleResult = testResults[i]) && currentBuildIndex < buildCount; i++) {
+            var count = rleResult[RLE.LENGTH];
+            var value = rleResult[RLE.VALUE];
+
+            for (var j = 0; j < count; j++) {
+                var buildTestResults = flakyDeltasByBuild[currentBuildIndex++];
+                function addFlakyDelta(key)
+                {
+                    if (!(key in buildTestResults))
+                        buildTestResults[key] = 0;
+                    buildTestResults[key]++;
+                }
+                addFlakyDelta(value);
+                if (value != 'P' && value != 'N')
+                    addFlakyDelta('total');
+                if (currentBuildIndex == buildCount)
+                    break;
+            }
+        }
+    }
+
+    return {
+        testNames: testNames,
+        resultsByBuild: resultsByBuild,
+        flakyTests: flakyTests,
+        flakyDeltasByBuild: flakyDeltasByBuild
+    };
+}
+
+document.addEventListener('keydown', function(e) {
+    if (g_currentBuildIndex == -1)
+        return;
+
+    var builder = g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder();
+    switch (e.keyIdentifier) {
+    case 'Left':
+        selectBuild(
+            g_resultsByBuilder[builder],
+            builder,
+            g_dygraph,
+            g_currentBuildIndex + 1);
+        break;
+    case 'Right':
+        selectBuild(
+            g_resultsByBuilder[builder],
+            builder,
+            g_dygraph,
+            g_currentBuildIndex - 1);
+        break;
+    }
+});
+
+window.addEventListener('load', function() {
+    var resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);
diff --git a/Tools/TestResultServer/static-dashboards/treemap.html b/Tools/TestResultServer/static-dashboards/treemap.html
index aa7ae43..acf9af9 100644
--- a/Tools/TestResultServer/static-dashboards/treemap.html
+++ b/Tools/TestResultServer/static-dashboards/treemap.html
@@ -109,256 +109,13 @@
 </style>
 <script src="builders.js"></script>
 <script src="loader.js"></script>
+<script src="string.js"></script>
+<script src="history.js"></script>
 <script src="dashboard_base.js"></script>
-<script src='webtreemap.js'></script>
+<script src="ui.js"></script>
+<script src="treemap.js"></script>
+<script src="webtreemap.js"></script>
 
 <div id='header-container'></div>
 <p>Click on a box to zoom in. Click on the outermost box to zoom out. <a href="" onclick="showAverages();return false;">Show averages</a></p>
 <div id='map'></div>
-
-<script>
-var TEST_URL_BASE_PATH = "http://svn.webkit.org/repository/webkit/trunk/";
-
-function humanReadableTime(milliseconds)
-{
-    if (milliseconds < 1000)
-        return Math.floor(milliseconds) + 'ms';
-    else if (milliseconds < 60000)
-        return (milliseconds / 1000).toPrecision(2) + 's';
-
-    var minutes = Math.floor(milliseconds / 60000);
-    var seconds = Math.floor((milliseconds - minutes * 60000) / 1000);
-    return minutes + 'm' + seconds + 's';
-}
-
-// This looks like:
-// { "data": {"$area": (sum of all timings)},
-//   "name": (name of this node),
-//   "children": [ (child nodes, in the same format as this) ] }
-// childCount is added just to be includes in the node's name
-function convertToWebTreemapFormat(treename, tree, path)
-{
-    var total = 0;
-    var childCount = 0;
-    var children = [];
-    for (var name in tree) {
-        var treeNode = tree[name];
-        if (typeof treeNode == "number") {
-            var time = treeNode;
-            var node = {
-                "data": {"$area": time},
-                "name": name + " (" + humanReadableTime(time) + ")"
-            };
-            children.push(node);
-            total += time;
-            childCount++;
-        } else {
-            var newPath = path ? path + '/' + name : name;
-            var subtree = convertToWebTreemapFormat(name, treeNode, newPath);
-            children.push(subtree);
-            total += subtree["data"]["$area"];
-            childCount += subtree["childCount"];
-        }
-    }
-
-    children.sort(function(a, b) {
-        aTime = a.data["$area"]
-        bTime = b.data["$area"]
-        return bTime - aTime;
-    });
-
-    return {
-        "data": {"$area": total},
-        "name": treename + " (" + humanReadableTime(total) + " - " + childCount + " tests)",
-        "children": children,
-        "childCount": childCount,
-        "path": path
-    };
-}
-
-function listOfAllNonLeafNodes(tree, list)
-{
-    if (!tree.children)
-        return;
-
-    if (!list)
-        list = [];
-    list.push(tree);
-
-    tree.children.forEach(function(child) {
-        listOfAllNonLeafNodes(child, list);
-    });
-    return list;
-}
-
-function reverseSortByAverage(list)
-{
-    list.sort(function(a, b) {
-        var avgA = a.data['$area'] / a.childCount;
-        var avgB = b.data['$area'] / b.childCount;
-        return avgB - avgA;
-    });
-}
-
-function showAverages()
-{
-    if (!document.getElementById('map'))
-        return;
-
-    var table = document.createElement('table');
-    table.innerHTML = '<th>directory</th><th># tests</th><th>avg time / test</th>';
-
-    var allNodes = listOfAllNonLeafNodes(g_webTree);
-    reverseSortByAverage(allNodes);
-    allNodes.forEach(function(node) {
-        var average = node.data['$area'] / node.childCount;
-        if (average > 100 && node.childCount != 1) {
-            var tr = document.createElement('tr');
-            tr.innerHTML = '<td></td><td>' + node.childCount + '</td><td>' + humanReadableTime(average) + '</td>';
-            tr.querySelector('td').innerText = node.path;
-            table.appendChild(tr);
-        }
-    });
-
-    var map = document.getElementById('map');
-    map.parentNode.replaceChild(table, map);
-}
-
-var g_isGeneratingPage = false;
-var g_webTree;
-
-function generatePage()
-{
-    $('header-container').innerHTML = htmlForTestTypeSwitcher();
-
-    g_isGeneratingPage = true;
-
-    var rawTree = g_resultsByBuilder[g_currentState.builder];
-    g_webTree = convertToWebTreemapFormat('LayoutTests', rawTree);
-    appendTreemap($('map'), g_webTree);
-
-    if (g_currentState.treemapfocus)
-        focusPath(g_webTree, g_currentState.treemapfocus)
-
-    g_isGeneratingPage = false;
-}
-
-function focusPath(tree, path)
-{
-    var parts = decodeURIComponent(path).split('/');
-    if (extractName(tree) != parts[0]) {
-        console.error('Could not focus tree rooted at ' + parts[0]);
-        return;
-    }
-
-    for (var i = 1; i < parts.length; i++) {
-        var children = tree.children;
-        for (var j = 0; j < children.length; j++) {
-            var child = children[j];
-            if (extractName(child) == parts[i]) {
-                tree = child;
-                focus(tree);
-                break;
-            }
-        }
-        if (j == children.length) {
-            console.error('Could not find tree at ' + parts[i]);
-            break;
-        }
-    }
-
-}
-
-function handleValidHashParameter(key, value)
-{
-    switch(key) {
-    case 'builder':
-        validateParameter(g_currentState, key, value,
-            function() { return value in g_builders; });
-        return true;
-
-    case 'treemapfocus':
-        validateParameter(g_currentState, key, value,
-            function() {
-                // FIXME: There's probably a simpler regexp here. Just trying to match ascii + forward-slash.
-                // e.g. LayoutTests/foo/bar.html
-                return (value.match(/^(\w+\/\w*)*$/));
-            });
-        return true;
-
-    default:
-        return false;
-    }
-}
-
-g_defaultDashboardSpecificStateValues = {
-    treemapfocus: '',
-}
-
-function handleQueryParameterChange(params)
-{
-    for (var param in params) {
-        if (param != 'treemapfocus') {
-            $('map').innerHTML = 'Loading...';
-            return true;
-        }
-    }
-    return false;
-}
-
-// Overrides handleResourceLoadError in dashboard_base.js.
-function handleResourceLoadError(builderName, e)
-{
-    $('map').innerHTML = '<span class=error>Could not load data for ' + builderName + '. ' +
-        'Either there was a server-side error or ' + builderName + ' does not run ' +
-        g_crossDashboardState.testType + '.</span>';
-}
-
-function extractName(node)
-{
-    return node.name.split(' ')[0];
-}
-
-function fullName(node)
-{
-    var buffer = [extractName(node)];
-    while (node.parent) {
-        node = node.parent;
-        buffer.unshift(extractName(node));
-    }
-    return buffer.join('/');
-}
-
-function handleFocus(tree)
-{
-    var currentlyFocusedNode = $('focused-leaf');
-    if (currentlyFocusedNode)
-        currentlyFocusedNode.id = '';
-
-    if (!tree.children)
-        tree.dom.id = 'focused-leaf';
-
-    var name = fullName(tree);
-
-    if (!tree.children && !tree.extraDom && isLayoutTestResults()) {
-        tree.extraDom = document.createElement('pre');
-        tree.extraDom.className = 'extra-dom';
-        tree.dom.appendChild(tree.extraDom);
-
-        loader.request(TEST_URL_BASE_PATH + name,
-            function(xhr) {
-                tree.extraDom.onmousedown = function(e) {
-                    e.stopPropagation();
-                };
-                tree.extraDom.textContent = xhr.responseText;
-            },
-            function (xhr) {
-                tree.extraDom.textContent = "Could not load test."
-        });
-    }
-
-    // We don't want the focus calls during generatePage to try to modify the query state.
-    if (!g_isGeneratingPage)
-        setQueryParameter('treemapfocus', name);
-}
-</script>
diff --git a/Tools/TestResultServer/static-dashboards/treemap.js b/Tools/TestResultServer/static-dashboards/treemap.js
new file mode 100644
index 0000000..a261dfd
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/treemap.js
@@ -0,0 +1,275 @@
+// Copyright (C) 2013 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.
+
+var TEST_URL_BASE_PATH = "http://svn.webkit.org/repository/webkit/trunk/";
+
+function humanReadableTime(milliseconds)
+{
+    if (milliseconds < 1000)
+        return Math.floor(milliseconds) + 'ms';
+    else if (milliseconds < 60000)
+        return (milliseconds / 1000).toPrecision(2) + 's';
+
+    var minutes = Math.floor(milliseconds / 60000);
+    var seconds = Math.floor((milliseconds - minutes * 60000) / 1000);
+    return minutes + 'm' + seconds + 's';
+}
+
+// This looks like:
+// { "data": {"$area": (sum of all timings)},
+//   "name": (name of this node),
+//   "children": [ (child nodes, in the same format as this) ] }
+// childCount is added just to be includes in the node's name
+function convertToWebTreemapFormat(treename, tree, path)
+{
+    var total = 0;
+    var childCount = 0;
+    var children = [];
+    for (var name in tree) {
+        var treeNode = tree[name];
+        if (typeof treeNode == "number") {
+            var time = treeNode;
+            var node = {
+                "data": {"$area": time},
+                "name": name + " (" + humanReadableTime(time) + ")"
+            };
+            children.push(node);
+            total += time;
+            childCount++;
+        } else {
+            var newPath = path ? path + '/' + name : name;
+            var subtree = convertToWebTreemapFormat(name, treeNode, newPath);
+            children.push(subtree);
+            total += subtree["data"]["$area"];
+            childCount += subtree["childCount"];
+        }
+    }
+
+    children.sort(function(a, b) {
+        aTime = a.data["$area"]
+        bTime = b.data["$area"]
+        return bTime - aTime;
+    });
+
+    return {
+        "data": {"$area": total},
+        "name": treename + " (" + humanReadableTime(total) + " - " + childCount + " tests)",
+        "children": children,
+        "childCount": childCount,
+        "path": path
+    };
+}
+
+function listOfAllNonLeafNodes(tree, list)
+{
+    if (!tree.children)
+        return;
+
+    if (!list)
+        list = [];
+    list.push(tree);
+
+    tree.children.forEach(function(child) {
+        listOfAllNonLeafNodes(child, list);
+    });
+    return list;
+}
+
+function reverseSortByAverage(list)
+{
+    list.sort(function(a, b) {
+        var avgA = a.data['$area'] / a.childCount;
+        var avgB = b.data['$area'] / b.childCount;
+        return avgB - avgA;
+    });
+}
+
+function showAverages()
+{
+    if (!document.getElementById('map'))
+        return;
+
+    var table = document.createElement('table');
+    table.innerHTML = '<th>directory</th><th># tests</th><th>avg time / test</th>';
+
+    var allNodes = listOfAllNonLeafNodes(g_webTree);
+    reverseSortByAverage(allNodes);
+    allNodes.forEach(function(node) {
+        var average = node.data['$area'] / node.childCount;
+        if (average > 100 && node.childCount != 1) {
+            var tr = document.createElement('tr');
+            tr.innerHTML = '<td></td><td>' + node.childCount + '</td><td>' + humanReadableTime(average) + '</td>';
+            tr.querySelector('td').innerText = node.path;
+            table.appendChild(tr);
+        }
+    });
+
+    var map = document.getElementById('map');
+    map.parentNode.replaceChild(table, map);
+}
+
+var g_isGeneratingPage = false;
+var g_webTree;
+
+function generatePage()
+{
+    $('header-container').innerHTML = ui.html.testTypeSwitcher();
+
+    g_isGeneratingPage = true;
+
+    var rawTree = g_resultsByBuilder[g_history.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder()];
+    g_webTree = convertToWebTreemapFormat('LayoutTests', rawTree);
+    appendTreemap($('map'), g_webTree);
+
+    if (g_history.dashboardSpecificState.treemapfocus)
+        focusPath(g_webTree, g_history.dashboardSpecificState.treemapfocus)
+
+    g_isGeneratingPage = false;
+}
+
+function focusPath(tree, path)
+{
+    var parts = decodeURIComponent(path).split('/');
+    if (extractName(tree) != parts[0]) {
+        console.error('Could not focus tree rooted at ' + parts[0]);
+        return;
+    }
+
+    for (var i = 1; i < parts.length; i++) {
+        var children = tree.children;
+        for (var j = 0; j < children.length; j++) {
+            var child = children[j];
+            if (extractName(child) == parts[i]) {
+                tree = child;
+                focus(tree);
+                break;
+            }
+        }
+        if (j == children.length) {
+            console.error('Could not find tree at ' + parts[i]);
+            break;
+        }
+    }
+
+}
+
+function handleValidHashParameter(key, value)
+{
+    switch(key) {
+    case 'builder':
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
+            function() { return value in currentBuilders(); });
+        return true;
+
+    case 'treemapfocus':
+        history.validateParameter(g_history.dashboardSpecificState, key, value,
+            function() {
+                // FIXME: There's probably a simpler regexp here. Just trying to match ascii + forward-slash.
+                // e.g. LayoutTests/foo/bar.html
+                return (value.match(/^(\w+\/\w*)*$/));
+            });
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+g_defaultDashboardSpecificStateValues = {
+    builder: null,
+    treemapfocus: '',
+};
+
+DB_SPECIFIC_INVALIDATING_PARAMETERS = {
+    'testType': 'builder',
+    'group': 'builder'
+};
+
+function handleQueryParameterChange(params)
+{
+    for (var param in params) {
+        if (param != 'treemapfocus') {
+            $('map').innerHTML = 'Loading...';
+            return true;
+        }
+    }
+    return false;
+}
+
+function extractName(node)
+{
+    return node.name.split(' ')[0];
+}
+
+function fullName(node)
+{
+    var buffer = [extractName(node)];
+    while (node.parent) {
+        node = node.parent;
+        buffer.unshift(extractName(node));
+    }
+    return buffer.join('/');
+}
+
+function handleFocus(tree)
+{
+    var currentlyFocusedNode = $('focused-leaf');
+    if (currentlyFocusedNode)
+        currentlyFocusedNode.id = '';
+
+    if (!tree.children)
+        tree.dom.id = 'focused-leaf';
+
+    var name = fullName(tree);
+
+    if (!tree.children && !tree.extraDom && g_history.isLayoutTestResults()) {
+        tree.extraDom = document.createElement('pre');
+        tree.extraDom.className = 'extra-dom';
+        tree.dom.appendChild(tree.extraDom);
+
+        loader.request(TEST_URL_BASE_PATH + name,
+            function(xhr) {
+                tree.extraDom.onmousedown = function(e) {
+                    e.stopPropagation();
+                };
+                tree.extraDom.textContent = xhr.responseText;
+            },
+            function (xhr) {
+                tree.extraDom.textContent = "Could not load test."
+        });
+    }
+
+    // We don't want the focus calls during generatePage to try to modify the query state.
+    if (!g_isGeneratingPage)
+        g_history.setQueryParameter('treemapfocus', name);
+}
+
+window.addEventListener('load', function() {
+    var resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);
diff --git a/Tools/TestResultServer/static-dashboards/ui.js b/Tools/TestResultServer/static-dashboards/ui.js
new file mode 100644
index 0000000..8907986
--- /dev/null
+++ b/Tools/TestResultServer/static-dashboards/ui.js
@@ -0,0 +1,225 @@
+// Copyright (C) 2013 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.
+
+var ui = ui || {};
+
+(function() {
+
+ui.popup = {};
+
+ui.popup.hide = function()
+{
+    var popup = $('popup');
+    if (popup) {
+        popup.parentNode.removeChild(popup);
+        document.removeEventListener('mousedown', ui.popup._handleMouseDown, false);
+    }
+}
+
+ui.popup.show = function(target, html)
+{
+    var popup = $('popup');
+    if (!popup) {
+        popup = document.createElement('div');
+        popup.id = 'popup';
+        document.body.appendChild(popup);
+        document.addEventListener('mousedown', ui.popup._handleMouseDown, false);
+    }
+
+    // Set html first so that we can get accurate size metrics on the popup.
+    popup.innerHTML = html;
+
+    var targetRect = target.getBoundingClientRect();
+
+    var x = Math.min(targetRect.left - 10, document.documentElement.clientWidth - popup.offsetWidth);
+    x = Math.max(0, x);
+    popup.style.left = x + document.body.scrollLeft + 'px';
+
+    var y = targetRect.top + targetRect.height;
+    if (y + popup.offsetHeight > document.documentElement.clientHeight)
+        y = targetRect.top - popup.offsetHeight;
+    y = Math.max(0, y);
+    popup.style.top = y + document.body.scrollTop + 'px';
+}
+
+ui.popup._handleMouseDown = function(e) {
+    // Clear the open popup, unless the click was inside the popup.
+    var popup = $('popup');
+    if (popup && e.target != popup && !(popup.compareDocumentPosition(e.target) & 16)) 
+        ui.popup.hide();    
+}
+
+ui.html = {};
+
+ui.html.checkbox = function(queryParameter, label, isChecked, opt_extraJavaScript)
+{
+    var js = opt_extraJavaScript || '';
+    return '<label style="padding-left: 2em">' +
+        '<input type="checkbox" onchange="g_history.toggleQueryParameter(\'' + queryParameter + '\');' + js + '" ' +
+            (isChecked ? 'checked' : '') + '>' + label +
+        '</label> ';
+}
+
+ui.html.select = function(label, queryParameter, options)
+{
+    var html = '<label style="padding-left: 2em">' + label + ': ' +
+        '<select onchange="g_history.setQueryParameter(\'' + queryParameter + '\', this[this.selectedIndex].value)">';
+
+    for (var i = 0; i < options.length; i++) {
+        var value = options[i];
+        html += '<option value="' + value + '" ' +
+            (g_history.queryParameterValue(queryParameter) == value ? 'selected' : '') +
+            '>' + value + '</option>'
+    }
+    html += '</select></label> ';
+    return html;
+}
+
+// Returns the HTML for the select element to switch to different testTypes.
+ui.html.testTypeSwitcher = function(opt_noBuilderMenu, opt_extraHtml, opt_includeNoneBuilder)
+{
+    var html = '<div style="border-bottom:1px dashed">';
+    html += '' +
+        ui.html._dashboardLink('Stats', 'aggregate_results.html') +
+        ui.html._dashboardLink('Timeline', 'timeline_explorer.html') +
+        ui.html._dashboardLink('Results', 'flakiness_dashboard.html') +
+        ui.html._dashboardLink('Treemap', 'treemap.html');
+
+    html += ui.html.select('Test type', 'testType', TEST_TYPES);
+
+    if (!opt_noBuilderMenu) {
+        var buildersForMenu = Object.keys(currentBuilders());
+        if (opt_includeNoneBuilder)
+            buildersForMenu.unshift('--------------');
+        html += ui.html.select('Builder', 'builder', buildersForMenu);
+    }
+
+    html += ui.html.select('Group', 'group',
+        Object.keys(currentBuilderGroupCategory()));
+
+    if (!history.isTreeMap())
+        html += ui.html.checkbox('showAllRuns', 'Show all runs', g_history.crossDashboardState.showAllRuns);
+
+    if (opt_extraHtml)
+        html += opt_extraHtml;
+    return html + '</div>';
+}
+
+ui.html._loadDashboard = function(fileName)
+{
+    var pathName = window.location.pathname;
+    pathName = pathName.substring(0, pathName.lastIndexOf('/') + 1);
+    window.location = pathName + fileName + window.location.hash;
+}
+
+ui.html._topLink = function(html, onClick, isSelected)
+{
+    var cssText = isSelected ? 'font-weight: bold;' : 'color:blue;text-decoration:underline;cursor:pointer;';
+    cssText += 'margin: 0 5px;';
+    return '<span style="' + cssText + '" onclick="' + onClick + '">' + html + '</span>';
+}
+
+ui.html._dashboardLink = function(html, fileName)
+{
+    var pathName = window.location.pathname;
+    var currentFileName = pathName.substring(pathName.lastIndexOf('/') + 1);
+    var isSelected = currentFileName == fileName;
+    var onClick = 'ui.html._loadDashboard(\'' + fileName + '\')';
+    return ui.html._topLink(html, onClick, isSelected);
+}
+
+ui.html._revisionLink = function(results, index, key, singleUrlTemplate, rangeUrlTemplate)
+{
+    var currentRevision = parseInt(results[key][index], 10);
+    var previousRevision = parseInt(results[key][index + 1], 10);
+
+    function singleUrl()
+    {
+        return singleUrlTemplate.replace('<rev>', currentRevision);
+    }
+
+    function rangeUrl()
+    {
+        return rangeUrlTemplate.replace('<rev1>', currentRevision).replace('<rev2>', previousRevision + 1);
+    }
+
+    if (currentRevision == previousRevision)
+        return 'At <a href="' + singleUrl() + '">r' + currentRevision    + '</a>';
+    else if (currentRevision - previousRevision == 1)
+        return '<a href="' + singleUrl() + '">r' + currentRevision    + '</a>';
+    else
+        return '<a href="' + rangeUrl() + '">r' + (previousRevision + 1) + ' to r' + currentRevision + '</a>';
+}
+
+ui.html.chromiumRevisionLink = function(results, index)
+{
+    return ui.html._revisionLink(
+        results,
+        index,
+        CHROME_REVISIONS_KEY,
+        'http://src.chromium.org/viewvc/chrome?view=rev&revision=<rev>',
+        'http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&range=<rev2>:<rev1>&mode=html');
+}
+
+ui.html.webKitRevisionLink = function(results, index)
+{
+    return ui.html._revisionLink(
+        results,
+        index,
+        WEBKIT_REVISIONS_KEY,
+        'http://trac.webkit.org/changeset/<rev>',
+        'http://trac.webkit.org/log/trunk/?rev=<rev1>&stop_rev=<rev2>&limit=100&verbose=on');
+}
+
+
+ui.Errors = function() {
+    this._messages = '';
+    // Element to display the errors within.
+    this._containerElement = null;
+}
+
+ui.Errors.prototype = {
+    show: function()
+    {
+        if (!this._containerElement) {
+            this._containerElement = document.createElement('H2');
+            this._containerElement.style.color = 'red';
+            this._containerElement.id = 'errors';
+            document.body.appendChild(this._containerElement);
+        }
+
+        this._containerElement.innerHTML = this._messages;
+    },
+    // Record a new error message.
+    addError: function(message)
+    {
+        this._messages += message + '<br>';
+    }
+}
+
+})();
\ No newline at end of file