Merge from Chromium at DEPS revision r207203

This commit was generated by merge_to_master.py.

Change-Id: Ia8a6c2a997232c94108d8937f8c2556f42be1c37
diff --git a/Tools/TestResultServer/app.yaml b/Tools/TestResultServer/app.yaml
index cac2232..e63a017 100644
--- a/Tools/TestResultServer/app.yaml
+++ b/Tools/TestResultServer/app.yaml
@@ -9,6 +9,10 @@
   static_files: robots.txt
   upload: robots.txt
 
+- url: /favicon.ico
+  static_files: favicon.ico
+  upload: favicon.ico
+
 - url: /stylesheets
   static_dir: stylesheets
 
diff --git a/Tools/TestResultServer/favicon.ico b/Tools/TestResultServer/favicon.ico
new file mode 100644
index 0000000..1d0cfed
--- /dev/null
+++ b/Tools/TestResultServer/favicon.ico
Binary files differ
diff --git a/Tools/TestResultServer/handlers/menu.py b/Tools/TestResultServer/handlers/menu.py
index 7b5403a..8c780df 100644
--- a/Tools/TestResultServer/handlers/menu.py
+++ b/Tools/TestResultServer/handlers/menu.py
@@ -32,6 +32,7 @@
 from google.appengine.ext.webapp import template
 
 dashboards = [
+    ["Overview", "/dashboards/overview.html"],
     ["Results", "/dashboards/flakiness_dashboard.html"],
     ["Timeline", "/dashboards/timeline_explorer.html"],
     ["Treemap", "/dashboards/treemap.html"],
diff --git a/Tools/TestResultServer/model/jsonresults_unittest.py b/Tools/TestResultServer/model/jsonresults_unittest.py
index c9b167d..db8f9d7 100755
--- a/Tools/TestResultServer/model/jsonresults_unittest.py
+++ b/Tools/TestResultServer/model/jsonresults_unittest.py
@@ -99,7 +99,6 @@
     "build_number": "3",
     "interrupted": false,
     "num_missing": 0,
-    "uses_expectations_file": true,
     "layout_tests_dir": "\/tmp\/cr\/src\/third_party\/WebKit\/LayoutTests",
     "version": 3,
     "builder_name": "Webkit",
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.css b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.css
index 063addf..5498dbe 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.css
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.css
@@ -48,24 +48,17 @@
     white-space: nowrap;
 }
 .forms {
-    display: -webkit-box;
-    -webkit-box-align: baseline;
-}
-.forms > * {
-    display: block;
+    display: -webkit-flex;
+    -webkit-align-items: baseline;
 }
 .forms span {
     padding: 0px 3px;
 }
-#tests-form {
-    display: -webkit-box;
-    -webkit-box-align: baseline;
-    -webkit-box-flex: 1;
-}
-#tests-form > * {
-    display: block;
-}
 #tests-input {
-    display: -webkit-box;
-    -webkit-box-flex: 1;
+    -webkit-flex: 1 auto;
+    overflow: hidden;
+    resize: none;
+    height: 1em;
+    border: 2px inset;
+    white-space: nowrap;
 }
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
index 082ddf2..c944b4c 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard.js
@@ -40,7 +40,6 @@
 var RELEASE_TIMEOUT = 6;
 var DEBUG_TIMEOUT = 12;
 var SLOW_MULTIPLIER = 5;
-var CHUNK_SIZE = 25;
 
 // FIXME: Figure out how to make this not be hard-coded.
 // Probably just include in the results.json files and get it from there.
@@ -339,6 +338,14 @@
 function individualTestsForSubstringList()
 {
     var testList = substringList();
+    // If listing a lot of tests, assume you've passed in an explicit list of tests
+    // instead of patterns to match against. The matching code below is super slow.
+    //
+    // Also, when showChrome is false, we're embedding the dashboard elsewhere and
+    // an explicit test list is passed in. In that case, we don't want
+    // a search for compositing/foo.html to also show virtual/softwarecompositing/foo.html.
+    if (testList.length > 10 || !g_history.dashboardSpecificState.showChrome)
+        return testList;
 
     // Put the tests into an object first and then move them into an array
     // as a way of deduping.
@@ -367,6 +374,7 @@
     var testsArray = [];
     for (var test in testsMap)
         testsArray.push(test);
+
     return testsArray;
 }
 
@@ -1145,14 +1153,16 @@
 function appendExpectations()
 {
     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) {
-        for (var i = 0, len = chunk.length; i < len; i++)
-            loadExpectations(chunk[i]);
-        postHeightChangedMessage();
-
-    }, hideLoadingUI, 10000);
+    g_chunkedActionState = {
+        items: expectations,
+        index: 0
+    }
+    performChunkedAction(function(expectation) {
+            loadExpectations(expectation);
+            postHeightChangedMessage();
+        },
+        hideLoadingUI,
+        expectations);
 }
 
 function hideLoadingUI()
@@ -1168,47 +1178,52 @@
     console.log('Number of tests: ' + tests.length);
     if (g_history.dashboardSpecificState.showChrome)
         appendHTML(htmlForNavBar());
-    performChunkedAction(tests, function(chunk) {
-        appendHTML(htmlForIndividualTests(chunk));
-    }, appendExpectations, 500);
+    performChunkedAction(function(test) {
+            appendHTML(htmlForIndividualTest(test));
+        },
+        appendExpectations,
+        tests);
     if (g_history.dashboardSpecificState.showChrome) {
         $('tests-input').value = g_history.dashboardSpecificState.tests;
         $('result-input').value = g_history.dashboardSpecificState.result;
     }
 }
 
-function performChunkedAction(tests, handleChunk, onComplete, timeout, opt_index) {
+var g_chunkedActionRequestId;
+function performChunkedAction(action, onComplete, items, opt_index) {
+    if (g_chunkedActionRequestId)
+        cancelAnimationFrame(g_chunkedActionRequestId);
+
     var index = opt_index || 0;
-    setTimeout(function() {
-        var chunk = Array.prototype.slice.call(tests, index * CHUNK_SIZE, (index + 1) * CHUNK_SIZE);
-        if (chunk.length) {
-            handleChunk(chunk);
-            performChunkedAction(tests, handleChunk, onComplete, timeout, ++index);
-        } else
+    g_chunkedActionRequestId = requestAnimationFrame(function() {
+        if (index < items.length) {
+            action(items[index]);
+            performChunkedAction(action, onComplete, items, ++index);
+        } else {
             onComplete();
-    // No need for a timeout on the first chunked action.
-    }, index ? timeout : 0);
+        }
+    });
 }
 
-function htmlForIndividualTests(tests)
+function htmlForIndividualTest(test)
 {
-    var testsHTML = [];
-    for (var i = 0; i < tests.length; i++) {
-        var test = tests[i];
-        var testNameHtml = '';
-        if (g_history.dashboardSpecificState.showChrome || tests.length > 1) {
-            if (g_history.isLayoutTestResults()) {
-                var suite = lookupVirtualTestSuite(test);
-                var base = suite ? baseTest(test, suite) : test;
-                var versionControlUrl = TEST_URL_BASE_PATH_FOR_BROWSING + base;
-                testNameHtml += '<h2>' + linkHTMLToOpenWindow(versionControlUrl, test) + '</h2>';
-            } else
-                testNameHtml += '<h2>' + test + '</h2>';
-        }
-
-        testsHTML.push(testNameHtml + htmlForIndividualTestOnAllBuildersWithResultsLinks(test));
+    var testNameHtml = '';
+    if (g_history.dashboardSpecificState.showChrome) {
+        if (g_history.isLayoutTestResults()) {
+            var suite = lookupVirtualTestSuite(test);
+            var base = suite ? baseTest(test, suite) : test;
+            var versionControlUrl = TEST_URL_BASE_PATH_FOR_BROWSING + base;
+            testNameHtml += '<h2>' + linkHTMLToOpenWindow(versionControlUrl, test) + '</h2>';
+        } else
+            testNameHtml += '<h2>' + test + '</h2>';
     }
-    return testsHTML.join('<hr>');
+
+    return testNameHtml + htmlForIndividualTestOnAllBuildersWithResultsLinks(test);
+}
+
+function setTestsParameter(input)
+{
+    g_history.setQueryParameter('tests', input.value);
 }
 
 function htmlForNavBar()
@@ -1219,13 +1234,13 @@
         '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="g_history.setQueryParameter(\'tests\', tests.value);' +
-        'return false;"><span>Show tests on all platforms: </span>' +
-        '<input name=tests ' +
+        '</form><span>Show tests on all platforms: </span>' +
+        // Use a textarea to avoid the 32k limit on the length of inputs.
+        '<textarea name=tests ' +
         'placeholder="Comma or space-separated list of tests or partial ' +
         'paths to show test results across all builders, e.g., ' +
-        'foo/bar.html,foo/baz,domstorage" id=tests-input></form>' +
+        'foo/bar.html,foo/baz,domstorage" id=tests-input onchange="setTestsParameter(this)" ' +
+        'onkeydown="if (event.keyCode == 13) { setTestsParameter(this); return false; }"></textarea>' +
         '<span class=link onclick="showLegend()">Show legend [type ?]</span></div>';
     return html;
 }
diff --git a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
index ac4d334..eb41772 100644
--- a/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
+++ b/Tools/TestResultServer/static-dashboards/flakiness_dashboard_unittests.js
@@ -166,34 +166,35 @@
         '</div>');
 });
 
-test('htmlForIndividualTests', 4, function() {
+test('individualTestsForSubstringList', 2, function() {
+    var builderName = 'WebKit Linux';
+    g_resultsByBuilder[builderName] = {
+        buildNumbers: [2, 1],
+        blinkRevision: [1234, 1233],
+        failure_map: FAILURE_MAP,
+        tests: {
+            'foo/one.html': { results: [1, 'F'], times: [1, 0] },
+            'virtual/foo/one.html': { results: [1, 'F'], times: [1, 0] },
+        }
+    };
+
+    g_history.dashboardSpecificState.showChrome = true;
+    var testToMatch = 'foo/one.html';
+    g_history.dashboardSpecificState.tests = testToMatch;
+    deepEqual(individualTestsForSubstringList(), [testToMatch, 'virtual/foo/one.html']);
+
+    g_history.dashboardSpecificState.showChrome = false;
+    deepEqual(individualTestsForSubstringList(), [testToMatch]);
+});
+
+test('htmlForIndividualTest', 2, function() {
     var historyInstance = resetGlobals();
     builders.loadBuildersList('@ToT Blink', 'layout-tests');
-    var test1 = 'foo/nonexistant.html';
-    var test2 = 'bar/nonexistant.html';
+    var test = 'foo/nonexistant.html';
 
     historyInstance.dashboardSpecificState.showChrome = false;
 
-    var tests = [test1, test2];
-    equal(htmlForIndividualTests(tests),
-        '<h2><a href="' + TEST_URL_BASE_PATH_FOR_BROWSING + 'foo/nonexistant.html" target="_blank">foo/nonexistant.html</a></h2>' +
-        htmlForIndividualTestOnAllBuilders(test1) +
-        '<div class=expectations test=foo/nonexistant.html>' +
-            '<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="' + TEST_URL_BASE_PATH_FOR_BROWSING + 'bar/nonexistant.html" target="_blank">bar/nonexistant.html</a></h2>' +
-        htmlForIndividualTestOnAllBuilders(test2) +
-        '<div class=expectations test=bar/nonexistant.html>' +
-            '<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) +
+    equal(htmlForIndividualTest(test), htmlForIndividualTestOnAllBuilders(test) +
         '<div class=expectations test=foo/nonexistant.html>' +
             '<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> | ' +
@@ -202,16 +203,9 @@
 
     historyInstance.dashboardSpecificState.showChrome = true;
 
-    equal(htmlForIndividualTests(tests),
+    equal(htmlForIndividualTest(test),
         '<h2><a href="' + TEST_URL_BASE_PATH_FOR_BROWSING + 'foo/nonexistant.html" target="_blank">foo/nonexistant.html</a></h2>' +
-        htmlForIndividualTestOnAllBuildersWithResultsLinks(test1));
-
-    tests = [test1, test2];
-    equal(htmlForIndividualTests(tests),
-        '<h2><a href="' + TEST_URL_BASE_PATH_FOR_BROWSING + 'foo/nonexistant.html" target="_blank">foo/nonexistant.html</a></h2>' +
-        htmlForIndividualTestOnAllBuildersWithResultsLinks(test1) + '<hr>' +
-        '<h2><a href="' + TEST_URL_BASE_PATH_FOR_BROWSING + 'bar/nonexistant.html" target="_blank">bar/nonexistant.html</a></h2>' +
-        htmlForIndividualTestOnAllBuildersWithResultsLinks(test2));
+        htmlForIndividualTestOnAllBuildersWithResultsLinks(test));
 });
 
 test('linkifyBugs', 4, function() {
diff --git a/Tools/TestResultServer/static-dashboards/history.js b/Tools/TestResultServer/static-dashboards/history.js
index 622b76d..ec18f7f 100644
--- a/Tools/TestResultServer/static-dashboards/history.js
+++ b/Tools/TestResultServer/static-dashboards/history.js
@@ -84,7 +84,8 @@
             errors.show();
             window.location.hash = window.location.hash.replace('master=' + paramsMap.master, '');
         } else {
-            paramsMap.group = builders.masters[paramsMap.master].groups[0];
+            var groupIndex = paramsMap.master == 'ChromiumWebkit' ? 1 : 0;
+            paramsMap.group = builders.masters[paramsMap.master].groups[groupIndex];
             window.location.hash = window.location.hash.replace('master=' + paramsMap.master, 'group=' + encodeURIComponent(paramsMap.group));
             delete paramsMap.master;
         }
diff --git a/Tools/TestResultServer/static-dashboards/timeline_explorer.js b/Tools/TestResultServer/static-dashboards/timeline_explorer.js
index 5756b89..5705dc7 100644
--- a/Tools/TestResultServer/static-dashboards/timeline_explorer.js
+++ b/Tools/TestResultServer/static-dashboards/timeline_explorer.js
@@ -182,14 +182,14 @@
     inspectorNode.style.visibility = 'visible';
 
     if (g_currentBuildIndex != -1)
-        selectBuild(results, builder, g_dygraph, g_currentBuildIndex);
+        selectBuild(resultsForBuilder, builder, g_dygraph, g_currentBuildIndex);
 }
 
 function selectBuild(resultsForBuilder, builder, dygraph, index)
 {
     g_currentBuildIndex = index;
-    updateBuildIndicator(results, dygraph);
-    updateBuildInspector(results, builder, dygraph, index);
+    updateBuildIndicator(resultsForBuilder, dygraph);
+    updateBuildInspector(resultsForBuilder, builder, dygraph, index);
     g_history.setQueryParameter('buildTimestamp', resultsForBuilder[results.TIMESTAMPS][index] * 1000);
 }