summaryrefslogtreecommitdiffstats
path: root/third_party/webkit/PerformanceTests/StyleBench/resources
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/webkit/PerformanceTests/StyleBench/resources')
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js103
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js299
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/gauge.png0
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/gauge@2x.png0
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/main.css286
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/main.js246
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.html32
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.js511
-rw-r--r--third_party/webkit/PerformanceTests/StyleBench/resources/tests.js40
9 files changed, 1517 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js
new file mode 100644
index 0000000000..a02619a6d1
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js
@@ -0,0 +1,103 @@
+// This file can be customized to report results as needed.
+
+(function () {
+ if (!window.testRunner && location.search != '?webkit' && location.hash != '#webkit' &&
+ location.search != '?gecko' && location.search != '?raptor')
+ return;
+
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ var scriptElement = document.createElement('script');
+ scriptElement.src = '../resources/runner.js';
+ document.head.appendChild(scriptElement);
+
+ var styleElement = document.createElement('style');
+ styleElement.textContent = 'pre { padding-top: 600px; }';
+ document.head.appendChild(styleElement);
+
+ var createTest;
+ var valuesByIteration = new Array;
+
+ window.onload = function () {
+ document.body.removeChild(document.querySelector('main'));
+ startBenchmark();
+ }
+
+ window.benchmarkClient = {
+ iterationCount: 5, // Use 4 different instances of DRT/WTR to run 5 iterations.
+ willStartFirstIteration: function (iterationCount) {
+ createTest = function (name, aggregator, isLastTest, unit = 'ms') {
+ return {
+ customIterationCount: iterationCount,
+ doNotIgnoreInitialRun: true,
+ doNotMeasureMemoryUsage: true,
+ continueTesting: !isLastTest,
+ unit: unit,
+ name: name,
+ aggregator: aggregator};
+ }
+ if (window.PerfTestRunner)
+ PerfTestRunner.prepareToMeasureValuesAsync(createTest(null, 'Geometric'));
+ },
+ didRunSuites: function (measuredValues) {
+ if (window.PerfTestRunner)
+ PerfTestRunner.measureValueAsync(measuredValues.geomean);
+ valuesByIteration.push(measuredValues);
+ },
+ didFinishLastIteration: function () {
+ document.head.removeChild(document.querySelector('style'));
+
+ var measuredValuesByFullName = {};
+ function addToMeasuredValue(value, fullName, aggregator) {
+ var values = measuredValuesByFullName[fullName] || new Array;
+ measuredValuesByFullName[fullName] = values;
+ values.push(value);
+ values.aggregator = aggregator;
+ }
+
+ var scores = [];
+ valuesByIteration.forEach(function (measuredValues) {
+ scores.push(measuredValues.score);
+ for (var suiteName in measuredValues.tests) {
+ var suite = measuredValues.tests[suiteName];
+ for (var testName in suite.tests) {
+ var test = suite.tests[testName];
+ for (var subtestName in test.tests)
+ addToMeasuredValue(test.tests[subtestName], suiteName + '/' + testName + '/' + subtestName);
+ addToMeasuredValue(test.total, suiteName + '/' + testName, 'Total');
+ }
+ addToMeasuredValue(suite.total, suiteName, 'Total');
+ }
+ });
+
+ if (window.PerfTestRunner)
+ PerfTestRunner.reportValues(createTest(null, null, false, 'pt'), scores);
+
+ var fullNames = new Array;
+ for (var fullName in measuredValuesByFullName)
+ fullNames.push(fullName);
+
+ if (location.search == '?raptor') {
+ var data = ['raptor-benchmark', 'speedometer', measuredValuesByFullName];
+ window.postMessage(data, '*');
+ window.sessionStorage.setItem('benchmark_results', JSON.stringify(data));
+ } else if (typeof tpRecordTime !== "undefined") {
+ var values = new Array;
+ var allNames = new Array;
+ for (var i = 0; i < fullNames.length; i++) {
+ var vals = measuredValuesByFullName[fullNames[i]];
+ values.push(vals);
+ for (var count = 0; count < vals.length; count ++)
+ allNames.push(fullNames[i]);
+ }
+ tpRecordTime(values.join(','), 0, allNames.join(','));
+ } else if (window.PerfTestRunner) {
+ for (var i = 0; i < fullNames.length; i++) {
+ var values = measuredValuesByFullName[fullNames[i]];
+ PerfTestRunner.reportValues(createTest(fullNames[i], values.aggregator, i + 1 == fullNames.length), values);
+ }
+ }
+ }
+ };
+})();
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js
new file mode 100644
index 0000000000..a248182594
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js
@@ -0,0 +1,299 @@
+// FIXME: Use the real promise if available.
+// FIXME: Make sure this interface is compatible with the real Promise.
+function SimplePromise() {
+ this._chainedPromise = null;
+ this._callback = null;
+}
+
+SimplePromise.prototype.then = function (callback) {
+ if (this._callback)
+ throw "SimplePromise doesn't support multiple calls to then";
+ this._callback = callback;
+ this._chainedPromise = new SimplePromise;
+
+ if (this._resolved)
+ this.resolve(this._resolvedValue);
+
+ return this._chainedPromise;
+}
+
+SimplePromise.prototype.resolve = function (value) {
+ if (!this._callback) {
+ this._resolved = true;
+ this._resolvedValue = value;
+ return;
+ }
+
+ var result = this._callback(value);
+ if (result instanceof SimplePromise) {
+ var chainedPromise = this._chainedPromise;
+ result.then(function (result) { chainedPromise.resolve(result); });
+ } else
+ this._chainedPromise.resolve(result);
+}
+
+function BenchmarkTestStep(testName, testFunction) {
+ this.name = testName;
+ this.run = testFunction;
+}
+
+function BenchmarkRunner(suites, client) {
+ this._suites = suites;
+ this._prepareReturnValue = null;
+ this._client = client;
+}
+
+BenchmarkRunner.prototype.waitForElement = function (selector) {
+ var promise = new SimplePromise;
+ var contentDocument = this._frame.contentDocument;
+
+ function resolveIfReady() {
+ var element = contentDocument.querySelector(selector);
+ if (element)
+ return promise.resolve(element);
+ setTimeout(resolveIfReady, 50);
+ }
+
+ resolveIfReady();
+ return promise;
+}
+
+BenchmarkRunner.prototype._removeFrame = function () {
+ if (this._frame) {
+ this._frame.parentNode.removeChild(this._frame);
+ this._frame = null;
+ }
+}
+
+BenchmarkRunner.prototype._appendFrame = function (src) {
+ var frame = document.createElement('iframe');
+ frame.style.width = '800px';
+ frame.style.height = '600px';
+ frame.style.border = '0px none';
+ frame.style.position = 'absolute';
+ frame.setAttribute('scrolling', 'no');
+
+ var marginLeft = parseInt(getComputedStyle(document.body).marginLeft);
+ var marginTop = parseInt(getComputedStyle(document.body).marginTop);
+ if (window.innerWidth > 800 + marginLeft && window.innerHeight > 600 + marginTop) {
+ frame.style.left = marginLeft + 'px';
+ frame.style.top = marginTop + 'px';
+ } else {
+ frame.style.left = '0px';
+ frame.style.top = '0px';
+ }
+
+ if (this._client && this._client.willAddTestFrame)
+ this._client.willAddTestFrame(frame);
+
+ document.body.insertBefore(frame, document.body.firstChild);
+ this._frame = frame;
+ return frame;
+}
+
+BenchmarkRunner.prototype._waitAndWarmUp = function () {
+ var startTime = Date.now();
+
+ function Fibonacci(n) {
+ if (Date.now() - startTime > 100)
+ return;
+ if (n <= 0)
+ return 0;
+ else if (n == 1)
+ return 1;
+ return Fibonacci(n - 2) + Fibonacci(n - 1);
+ }
+
+ var promise = new SimplePromise;
+ setTimeout(function () {
+ Fibonacci(100);
+ promise.resolve();
+ }, 200);
+ return promise;
+}
+
+BenchmarkRunner.prototype._writeMark = function(name) {
+ if (window.performance && window.performance.mark)
+ window.performance.mark(name);
+}
+
+// This function ought be as simple as possible. Don't even use SimplePromise.
+BenchmarkRunner.prototype._runTest = function(suite, test, prepareReturnValue, callback)
+{
+ var self = this;
+ var now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now;
+
+ var contentWindow = self._frame.contentWindow;
+ var contentDocument = self._frame.contentDocument;
+
+ self._writeMark(suite.name + '.' + test.name + '-start');
+ var startTime = now();
+ test.run(prepareReturnValue, contentWindow, contentDocument);
+ var endTime = now();
+ self._writeMark(suite.name + '.' + test.name + '-sync-end');
+
+ var syncTime = endTime - startTime;
+
+ var startTime = now();
+ setTimeout(function () {
+ // Some browsers don't immediately update the layout for paint.
+ // Force the layout here to ensure we're measuring the layout time.
+ var height = self._frame.contentDocument.body.getBoundingClientRect().height;
+ var endTime = now();
+ self._frame.contentWindow._unusedHeightValue = height; // Prevent dead code elimination.
+ self._writeMark(suite.name + '.' + test.name + '-async-end');
+ callback(syncTime, endTime - startTime, height);
+ }, 0);
+}
+
+function BenchmarkState(suites) {
+ this._suites = suites;
+ this._suiteIndex = -1;
+ this._testIndex = 0;
+ this.next();
+}
+
+BenchmarkState.prototype.currentSuite = function() {
+ return this._suites[this._suiteIndex];
+}
+
+BenchmarkState.prototype.currentTest = function () {
+ var suite = this.currentSuite();
+ return suite ? suite.tests[this._testIndex] : null;
+}
+
+BenchmarkState.prototype.next = function () {
+ this._testIndex++;
+
+ var suite = this._suites[this._suiteIndex];
+ if (suite && this._testIndex < suite.tests.length)
+ return this;
+
+ this._testIndex = 0;
+ do {
+ this._suiteIndex++;
+ } while (this._suiteIndex < this._suites.length && this._suites[this._suiteIndex].disabled);
+
+ return this;
+}
+
+BenchmarkState.prototype.isFirstTest = function () {
+ return !this._testIndex;
+}
+
+BenchmarkState.prototype.prepareCurrentSuite = function (runner, frame) {
+ var suite = this.currentSuite();
+ var promise = new SimplePromise;
+ frame.onload = function () {
+ suite.prepare(runner, frame.contentWindow, frame.contentDocument).then(function (result) { promise.resolve(result); });
+ }
+ frame.src = 'resources/' + suite.url;
+ return promise;
+}
+
+BenchmarkRunner.prototype.step = function (state) {
+ if (!state) {
+ state = new BenchmarkState(this._suites);
+ this._measuredValues = {tests: {}, total: 0, mean: NaN, geomean: NaN, score: NaN};
+ }
+
+ var suite = state.currentSuite();
+ if (!suite) {
+ this._finalize();
+ var promise = new SimplePromise;
+ promise.resolve();
+ return promise;
+ }
+
+ if (state.isFirstTest()) {
+ this._removeFrame();
+ var self = this;
+ return state.prepareCurrentSuite(this, this._appendFrame()).then(function (prepareReturnValue) {
+ self._prepareReturnValue = prepareReturnValue;
+ return self._runTestAndRecordResults(state);
+ });
+ }
+
+ return this._runTestAndRecordResults(state);
+}
+
+BenchmarkRunner.prototype.runAllSteps = function (startingState) {
+ var nextCallee = this.runAllSteps.bind(this);
+ this.step(startingState).then(function (nextState) {
+ if (nextState)
+ nextCallee(nextState);
+ });
+}
+
+BenchmarkRunner.prototype.runMultipleIterations = function (iterationCount) {
+ var self = this;
+ var currentIteration = 0;
+
+ this._runNextIteration = function () {
+ currentIteration++;
+ if (currentIteration < iterationCount)
+ self.runAllSteps();
+ else if (this._client && this._client.didFinishLastIteration)
+ this._client.didFinishLastIteration();
+ }
+
+ if (this._client && this._client.willStartFirstIteration)
+ this._client.willStartFirstIteration(iterationCount);
+
+ self.runAllSteps();
+}
+
+BenchmarkRunner.prototype._runTestAndRecordResults = function (state) {
+ var promise = new SimplePromise;
+ var suite = state.currentSuite();
+ var test = state.currentTest();
+
+ if (this._client && this._client.willRunTest)
+ this._client.willRunTest(suite, test);
+
+ var self = this;
+ setTimeout(function () {
+ self._runTest(suite, test, self._prepareReturnValue, function (syncTime, asyncTime) {
+ var suiteResults = self._measuredValues.tests[suite.name] || {tests:{}, total: 0};
+ var total = syncTime + asyncTime;
+ self._measuredValues.tests[suite.name] = suiteResults;
+ suiteResults.tests[test.name] = {tests: {'Sync': syncTime, 'Async': asyncTime}, total: total};
+ suiteResults.total += total;
+
+ if (self._client && self._client.didRunTest)
+ self._client.didRunTest(suite, test);
+
+ state.next();
+ promise.resolve(state);
+ });
+ }, 0);
+ return promise;
+}
+
+BenchmarkRunner.prototype._finalize = function () {
+ this._removeFrame();
+
+ if (this._client && this._client.didRunSuites) {
+ var product = 1;
+ var values = [];
+ for (var suiteName in this._measuredValues.tests) {
+ var suiteTotal = this._measuredValues.tests[suiteName].total;
+ product *= suiteTotal;
+ values.push(suiteTotal);
+ }
+
+ values.sort(function (a, b) { return a - b }); // Avoid the loss of significance for the sum.
+ var total = values.reduce(function (a, b) { return a + b });
+ var geomean = Math.pow(product, 1 / values.length);
+
+ var correctionFactor = 1.5; // This factor makes the test score look reasonably fit within 0 to 140.
+ this._measuredValues.total = total;
+ this._measuredValues.mean = total / values.length;
+ this._measuredValues.geomean = geomean;
+ this._measuredValues.score = 60 * 1000 / geomean / correctionFactor;
+ this._client.didRunSuites(this._measuredValues);
+ }
+
+ if (this._runNextIteration)
+ this._runNextIteration();
+}
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/gauge.png b/third_party/webkit/PerformanceTests/StyleBench/resources/gauge.png
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/gauge.png
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/gauge@2x.png b/third_party/webkit/PerformanceTests/StyleBench/resources/gauge@2x.png
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/gauge@2x.png
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/main.css b/third_party/webkit/PerformanceTests/StyleBench/resources/main.css
new file mode 100644
index 0000000000..126cdcdbca
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/main.css
@@ -0,0 +1,286 @@
+body {
+ background-color: rgb(46, 51, 55);
+ color: rgb(235, 235, 235);
+ font-family: "Helvetica Neue", Helvetica, Verdana, sans-serif;
+}
+
+::selection {
+ color: rgb(46, 51, 55);
+ background-color: rgb(235, 235, 235);
+}
+
+h1,
+button {
+ font-family: "Futura-Medium", Futura, "Helvetica Neue", Helvetica, Verdana, sans-serif;
+}
+
+code {
+ font-family: Menlo, Monaco, monospace;
+ font-size: smaller;
+}
+
+button {
+ cursor: pointer;
+}
+
+hr {
+ border: 1px solid rgb(235, 235, 235);
+ width: 50%;
+ margin: 40px auto;
+}
+
+img {
+ -webkit-user-select: none;
+ -webkit-user-drag: none;
+}
+
+main {
+ display: block;
+ position: absolute;
+ width: 800px;
+ height: 600px;
+ top: 50%;
+ left: 50%;
+ margin-top: -321px;
+ margin-left: -421px;
+ padding: 15px;
+ border: 6px solid rgb(235, 235, 235);
+ border-radius: 20px;
+}
+
+#logo {
+ position: absolute;
+ left: -70px;
+ top: 115px;
+ width: 75px;
+ height: 406px;
+}
+
+h1 {
+ margin-top: 30px;
+ font-size: 40px;
+ font-weight: normal;
+ color: rgb(235, 235, 235);
+ text-align: center;
+}
+
+p {
+ font-size: 16px;
+ line-height: 21px;
+}
+
+a {
+ color: inherit;
+}
+
+.buttons {
+ margin-top: 30px;
+ text-align: center;
+}
+
+button {
+ -webkit-appearance: none;
+ border: 3px solid rgb(235, 235, 235);
+ border-radius: 10px;
+ min-width: 200px;
+ padding: 5px 20px;
+ margin: 0 40px;
+ font-size: 25px;
+ color: rgb(235, 235, 235);
+ background-color: transparent;
+
+ -webkit-user-select: none;
+}
+
+button:active {
+ background-color: rgb(235, 235, 235);
+ color: rgb(46, 51, 55);
+ border-color: rgb(235, 235, 235) !important;
+}
+
+button:focus {
+ outline: none;
+ border-color: rgb(232, 79, 79);
+}
+
+section {
+ display: none;
+}
+
+section > p {
+ margin: 10px 20px;
+}
+
+section.selected {
+ display: block;
+}
+
+#testContainer {
+ position: absolute;
+ top: 15px;
+ left: 15px;
+ width: 800px;
+ height: 600px;
+}
+
+section#home > p {
+ margin: 0 auto;
+ width: 70%;
+ text-align: center;
+}
+
+section#home > p:first-child {
+ margin-top: 160px;
+ text-align: center;
+}
+
+section#home > .show-about {
+ margin-top: 100px;
+}
+
+section#home > .buttons {
+ margin-top: 80px;
+}
+
+section#running > #progress {
+ position: absolute;
+ bottom: -6px;
+ left: 60px;
+ right: 60px;
+ height: 6px;
+ background-color: rgb(128, 128, 128);
+ border-left: 6px solid rgb(46, 51, 55);
+ border-right: 6px solid rgb(46, 51, 55);
+}
+
+section#running #progress-completed {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 6px;
+ width: 0;
+ background-color: rgb(235, 235, 235);
+}
+
+section#running > #info {
+ position: absolute;
+ bottom: -25px;
+ left: 60px;
+ right: 60px;
+ height: 12px;
+ color: rgb(128, 128, 128);
+ text-align: center;
+ font-size: 12px;
+}
+
+section#summarized-results > #result-number,
+section#summarized-results > #confidence-number {
+ font-family: "Futura-CondensedMedium", Futura, "Helvetica Neue", Helvetica, Verdana, sans-serif;
+}
+
+section#summarized-results > #result-number {
+ text-align: center;
+ font-size: 145px;
+ line-height: 145px;
+}
+
+section#summarized-results > #confidence-number {
+ text-align: center;
+ font-size: 36px;
+ line-height: 36px;
+ color: rgb(128, 128, 128);
+}
+
+section#detailed-results > table {
+ float: left;
+ width: 50%;
+}
+
+section#detailed-results > .arithmetic-mean {
+ clear: both;
+ padding-top: 32px;
+ text-align: center;
+}
+
+section#detailed-results > .arithmetic-mean > label {
+ font-weight: bold;
+ margin-right: 10px;
+ color: rgb(128, 128, 128);
+}
+
+section#detailed-results > .show-about {
+ margin-top: 30px;
+ text-align: center;
+}
+
+section#about h1 {
+ margin-top: 10px;
+ margin-bottom: 0px;
+ font-size: 25px;
+}
+
+section#about .note {
+ color: rgb(128, 128, 128);
+}
+
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+
+th,
+td {
+ padding: 5px;
+}
+
+th {
+ text-align: right;
+ color: rgb(128, 128, 128);
+}
+
+.gauge {
+ position: relative;
+ width: 738px;
+ height: 78px;
+ background-image: url(gauge.png);
+ background-size: 100% 100%;
+ background-repat: no-repeat;
+ margin: 0 auto;
+}
+
+.gauge > .window {
+ position: absolute;
+ left: 0;
+ top: 33px;
+ bottom: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+.gauge > .window > .needle {
+ position: absolute;
+ left: 363px;
+ bottom: -88px;
+ width: 4px;
+ height: 400px;
+ background-color: rgb(247, 148, 29);
+
+ -webkit-transform: rotate(-70deg);
+ -webkit-transform-origin: 2px 400px;
+
+ -moz-transform: rotate(-70deg);
+ -moz-transform-origin: 2px 400px;
+
+ transform: rotate(-70deg);
+ transform-origin: 2px 400px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (min-resolution: 2dppx), (min-resolution: 192dpi) {
+ #logo {
+ content: url(logo@2x.png); /* FIXME: This does not work in Firefox. */
+ }
+
+ .gauge {
+ background-image: url(gauge@2x.png);
+ }
+}
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/main.js b/third_party/webkit/PerformanceTests/StyleBench/resources/main.js
new file mode 100644
index 0000000000..2ab877744b
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/main.js
@@ -0,0 +1,246 @@
+window.benchmarkClient = {
+ displayUnit: 'runs/min',
+ iterationCount: 10,
+ stepCount: null,
+ suitesCount: null,
+ _measuredValuesList: [],
+ _finishedTestCount: 0,
+ _progressCompleted: null,
+ willAddTestFrame: function (frame) {
+ var main = document.querySelector('main');
+ var style = getComputedStyle(main);
+ frame.style.left = main.offsetLeft + parseInt(style.borderLeftWidth) + parseInt(style.paddingLeft) + 'px';
+ frame.style.top = main.offsetTop + parseInt(style.borderTopWidth) + parseInt(style.paddingTop) + 'px';
+ },
+ willRunTest: function (suite, test) {
+ document.getElementById('info').textContent = suite.name + ' ( ' + this._finishedTestCount + ' / ' + this.stepCount + ' )';
+ },
+ didRunTest: function () {
+ this._finishedTestCount++;
+ this._progressCompleted.style.width = (this._finishedTestCount * 100 / this.stepCount) + '%';
+ },
+ didRunSuites: function (measuredValues) {
+ this._measuredValuesList.push(measuredValues);
+ },
+ willStartFirstIteration: function () {
+ this._measuredValuesList = [];
+ this._finishedTestCount = 0;
+ this._progressCompleted = document.getElementById('progress-completed');
+ document.getElementById('logo-link').onclick = function (event) { event.preventDefault(); return false; }
+ },
+ didFinishLastIteration: function () {
+ document.getElementById('logo-link').onclick = null;
+
+ var results = this._computeResults(this._measuredValuesList, this.displayUnit);
+
+ this._updateGaugeNeedle(results.mean);
+ document.getElementById('result-number').textContent = results.formattedMean;
+ if (results.formattedDelta)
+ document.getElementById('confidence-number').textContent = '\u00b1 ' + results.formattedDelta;
+
+ this._populateDetailedResults(results.formattedValues);
+ document.getElementById('results-with-statistics').textContent = results.formattedMeanAndDelta;
+
+ if (this.displayUnit == 'ms') {
+ document.getElementById('show-summary').style.display = 'none';
+ showResultDetails();
+ } else
+ showResultsSummary();
+ },
+ _computeResults: function (measuredValuesList, displayUnit) {
+ var suitesCount = this.suitesCount;
+ function valueForUnit(measuredValues) {
+ if (displayUnit == 'ms')
+ return measuredValues.geomean;
+ return measuredValues.score;
+ }
+
+ function sigFigFromPercentDelta(percentDelta) {
+ return Math.ceil(-Math.log(percentDelta)/Math.log(10)) + 3;
+ }
+
+ function toSigFigPrecision(number, sigFig) {
+ var nonDecimalDigitCount = number < 1 ? 0 : (Math.floor(Math.log(number)/Math.log(10)) + 1);
+ return number.toPrecision(Math.max(nonDecimalDigitCount, Math.min(6, sigFig)));
+ }
+
+ var values = measuredValuesList.map(valueForUnit);
+ var sum = values.reduce(function (a, b) { return a + b; }, 0);
+ var arithmeticMean = sum / values.length;
+ var meanSigFig = 4;
+ var formattedDelta;
+ var formattedPercentDelta;
+ if (window.Statistics) {
+ var delta = Statistics.confidenceIntervalDelta(0.95, values.length, sum, Statistics.squareSum(values));
+ if (!isNaN(delta)) {
+ var percentDelta = delta * 100 / arithmeticMean;
+ meanSigFig = sigFigFromPercentDelta(percentDelta);
+ formattedDelta = toSigFigPrecision(delta, 2);
+ formattedPercentDelta = toSigFigPrecision(percentDelta, 2) + '%';
+ }
+ }
+
+ var formattedMean = toSigFigPrecision(arithmeticMean, Math.max(meanSigFig, 3));
+
+ return {
+ formattedValues: values.map(function (value) {
+ return toSigFigPrecision(value, 4) + ' ' + displayUnit;
+ }),
+ mean: arithmeticMean,
+ formattedMean: formattedMean,
+ formattedDelta: formattedDelta,
+ formattedMeanAndDelta: formattedMean + (formattedDelta ? ' \xb1 ' + formattedDelta + ' (' + formattedPercentDelta + ')' : ''),
+ };
+ },
+ _addDetailedResultsRow: function (table, iterationNumber, value) {
+ var row = document.createElement('tr');
+ var th = document.createElement('th');
+ th.textContent = 'Iteration ' + (iterationNumber + 1);
+ var td = document.createElement('td');
+ td.textContent = value;
+ row.appendChild(th);
+ row.appendChild(td);
+ table.appendChild(row);
+ },
+ _updateGaugeNeedle: function (rpm) {
+ var needleAngle = Math.max(0, Math.min(rpm, 140)) - 70;
+ var needleRotationValue = 'rotate(' + needleAngle + 'deg)';
+
+ var gaugeNeedleElement = document.querySelector('#summarized-results > .gauge .needle');
+ gaugeNeedleElement.style.setProperty('-webkit-transform', needleRotationValue);
+ gaugeNeedleElement.style.setProperty('-moz-transform', needleRotationValue);
+ gaugeNeedleElement.style.setProperty('-ms-transform', needleRotationValue);
+ gaugeNeedleElement.style.setProperty('transform', needleRotationValue);
+ },
+ _populateDetailedResults: function (formattedValues) {
+ var resultsTables = document.querySelectorAll('.results-table');
+ var i = 0;
+ resultsTables[0].innerHTML = '';
+ for (; i < Math.ceil(formattedValues.length / 2); i++)
+ this._addDetailedResultsRow(resultsTables[0], i, formattedValues[i]);
+ resultsTables[1].innerHTML = '';
+ for (; i < formattedValues.length; i++)
+ this._addDetailedResultsRow(resultsTables[1], i, formattedValues[i]);
+ },
+ prepareUI: function () {
+ window.addEventListener('popstate', function (event) {
+ if (event.state) {
+ var sectionToShow = event.state.section;
+ if (sectionToShow) {
+ var sections = document.querySelectorAll('main > section');
+ for (var i = 0; i < sections.length; i++) {
+ if (sections[i].id === sectionToShow)
+ return showSection(sectionToShow, false);
+ }
+ }
+ }
+ return showSection('home', false);
+ }, false);
+
+ function updateScreenSize() {
+ // FIXME: Detect when the window size changes during the test.
+ var screenIsTooSmall = window.innerWidth < 850 || window.innerHeight < 650;
+ document.getElementById('screen-size').textContent = window.innerWidth + 'px by ' + window.innerHeight + 'px';
+ document.getElementById('screen-size-warning').style.display = screenIsTooSmall ? null : 'none';
+ }
+
+ window.addEventListener('resize', updateScreenSize);
+ updateScreenSize();
+ }
+}
+
+function enableOneSuite(suites, suiteToEnable)
+{
+ suiteToEnable = suiteToEnable.toLowerCase();
+ var found = false;
+ for (var i = 0; i < suites.length; i++) {
+ var currentSuite = suites[i];
+ if (currentSuite.name.toLowerCase() == suiteToEnable) {
+ currentSuite.disabled = false;
+ found = true;
+ } else
+ currentSuite.disabled = true;
+ }
+ return found;
+}
+
+function startBenchmark() {
+ if (location.search.length > 1) {
+ var parts = location.search.substring(1).split('&');
+ for (var i = 0; i < parts.length; i++) {
+ var keyValue = parts[i].split('=');
+ var key = keyValue[0];
+ var value = keyValue[1];
+ switch (key) {
+ case 'unit':
+ if (value == 'ms')
+ benchmarkClient.displayUnit = 'ms';
+ else
+ console.error('Invalid unit: ' + value);
+ break;
+ case 'iterationCount':
+ var parsedValue = parseInt(value);
+ if (!isNaN(parsedValue))
+ benchmarkClient.iterationCount = parsedValue;
+ else
+ console.error('Invalid iteration count: ' + value);
+ break;
+ case 'suite':
+ if (!enableOneSuite(Suites, value)) {
+ alert('Suite "' + value + '" does not exist. No tests to run.');
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ var enabledSuites = Suites.filter(function (suite) { return !suite.disabled; });
+ var totalSubtestsCount = enabledSuites.reduce(function (testsCount, suite) { return testsCount + suite.tests.length; }, 0);
+ benchmarkClient.stepCount = benchmarkClient.iterationCount * totalSubtestsCount;
+ benchmarkClient.suitesCount = enabledSuites.length;
+ var runner = new BenchmarkRunner(Suites, benchmarkClient);
+ runner.runMultipleIterations(benchmarkClient.iterationCount);
+
+ return true;
+}
+
+function showSection(sectionIdentifier, pushState) {
+ var currentSectionElement = document.querySelector('section.selected');
+ console.assert(currentSectionElement);
+
+ var newSectionElement = document.getElementById(sectionIdentifier);
+ console.assert(newSectionElement);
+
+ currentSectionElement.classList.remove('selected');
+ newSectionElement.classList.add('selected');
+
+ if (pushState)
+ history.pushState({section: sectionIdentifier}, document.title);
+}
+
+function showHome() {
+ showSection('home', true);
+}
+
+function startTest() {
+ if (startBenchmark())
+ showSection('running');
+}
+
+function showResultsSummary() {
+ showSection('summarized-results', true);
+}
+
+function showResultDetails() {
+ showSection('detailed-results', true);
+}
+
+function showAbout() {
+ showSection('about', true);
+}
+
+window.addEventListener('DOMContentLoaded', function () {
+ if (benchmarkClient.prepareUI)
+ benchmarkClient.prepareUI();
+});
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.html b/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.html
new file mode 100644
index 0000000000..ce55e10e3b
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script src="style-bench.js"></script>
+<body>
+<div id="testroot"></div>
+<div id="controls">
+<select></select>
+<button onclick="createBenchmarkFromSelect()">Initialize</button>
+<button onclick="createBenchmarkFromSelect().runForever()">Initialize and run</button>
+</div>
+<script>
+const configurations = StyleBench.predefinedConfigurations();
+
+const select = document.querySelector("#controls select");
+for (const configuration of configurations) {
+ const option = document.createElement("option");
+ option.innerHTML = configuration.name;
+ select.appendChild(option);
+}
+
+function createBenchmark(configuration)
+{
+ controls.remove();
+
+ return new StyleBench(configuration);
+}
+
+function createBenchmarkFromSelect()
+{
+ return createBenchmark(configurations[select.selectedIndex]);
+}
+</script>
+
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.js b/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.js
new file mode 100644
index 0000000000..f2dc17596f
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.js
@@ -0,0 +1,511 @@
+class Random
+{
+ constructor(seed)
+ {
+ this.seed = seed % 2147483647;
+ if (this.seed <= 0)
+ this.seed += 2147483646;
+ }
+
+ get next()
+ {
+ return this.seed = this.seed * 16807 % 2147483647;
+ }
+
+ underOne()
+ {
+ return (this.next % 1048576) / 1048576;
+ }
+
+ chance(chance)
+ {
+ return this.underOne() < chance;
+ }
+
+ number(under)
+ {
+ return this.next % under;
+ }
+
+ numberSquareWeightedToLow(under)
+ {
+ const random = this.underOne();
+ const random2 = random * random;
+ return Math.floor(random2 * under);
+ }
+}
+
+function nextAnimationFrame()
+{
+ return new Promise(resolve => requestAnimationFrame(resolve));
+}
+
+class StyleBench
+{
+ static defaultConfiguration()
+ {
+ return {
+ name: 'Default',
+ elementTypeCount: 10,
+ idChance: 0.05,
+ elementChance: 0.5,
+ classCount: 200,
+ classChance: 0.3,
+ starChance: 0.05,
+ attributeChance: 0.02,
+ attributeCount: 10,
+ attributeValueCount: 20,
+ attributeOperators: ['','='],
+ elementClassChance: 0.5,
+ elementMaximumClasses: 3,
+ elementAttributeChance: 0.2,
+ elementMaximumAttributes: 3,
+ combinators: [' ', '>',],
+ pseudoClasses: [],
+ pseudoClassChance: 0,
+ beforeAfterChance: 0,
+ maximumSelectorLength: 6,
+ ruleCount: 5000,
+ elementCount: 20000,
+ maximumTreeDepth: 6,
+ maximumTreeWidth: 50,
+ repeatingSequenceChance: 0.2,
+ repeatingSequenceMaximumLength: 3,
+ leafMutationChance: 0.1,
+ styleSeed: 1,
+ domSeed: 2,
+ stepCount: 5,
+ mutationsPerStep: 100,
+ };
+ }
+
+ static descendantCombinatorConfiguration()
+ {
+ return Object.assign(this.defaultConfiguration(), {
+ name: 'Descendant and child combinators',
+ });
+ }
+
+ static siblingCombinatorConfiguration()
+ {
+ return Object.assign(this.defaultConfiguration(), {
+ name: 'Sibling combinators',
+ combinators: [' ', ' ', '>', '>', '~', '+',],
+ });
+ }
+
+ static structuralPseudoClassConfiguration()
+ {
+ return Object.assign(this.defaultConfiguration(), {
+ name: 'Structural pseudo classes',
+ pseudoClassChance: 0.1,
+ pseudoClasses: [
+ 'first-child',
+ 'last-child',
+ 'first-of-type',
+ 'last-of-type',
+ 'only-of-type',
+ 'empty',
+ ],
+ });
+ }
+
+ static nthPseudoClassConfiguration()
+ {
+ return Object.assign(this.defaultConfiguration(), {
+ name: 'Nth pseudo classes',
+ pseudoClassChance: 0.1,
+ pseudoClasses: [
+ 'nth-child(2n+1)',
+ 'nth-last-child(3n)',
+ 'nth-of-type(3n)',
+ 'nth-last-of-type(4n)',
+ ],
+ });
+ }
+
+ static beforeAndAfterConfiguration()
+ {
+ return Object.assign(this.defaultConfiguration(), {
+ name: 'Before and after pseudo elements',
+ beforeAfterChance: 0.1,
+ });
+ }
+
+ static predefinedConfigurations()
+ {
+ return [
+ this.descendantCombinatorConfiguration(),
+ this.siblingCombinatorConfiguration(),
+ this.structuralPseudoClassConfiguration(),
+ this.nthPseudoClassConfiguration(),
+ this.beforeAndAfterConfiguration(),
+ ];
+ }
+
+ constructor(configuration)
+ {
+ this.configuration = configuration;
+ this.idCount = 0;
+
+ this.baseStyle = document.createElement("style");
+ this.baseStyle.textContent = `
+ #testroot {
+ font-size: 10px;
+ line-height: 10px;
+ }
+ #testroot * {
+ display: inline-block;
+ height:10px;
+ min-width:10px;
+ }
+ `;
+ document.head.appendChild(this.baseStyle);
+
+ this.random = new Random(this.configuration.styleSeed);
+ this.makeStyle();
+
+ this.random = new Random(this.configuration.domSeed);
+ this.makeTree();
+ }
+
+ randomElementName()
+ {
+ const elementTypeCount = this.configuration.elementTypeCount;
+ return `elem${ this.random.numberSquareWeightedToLow(elementTypeCount) }`;
+ }
+
+ randomClassName()
+ {
+ const classCount = this.configuration.classCount;
+ return `class${ this.random.numberSquareWeightedToLow(classCount) }`;
+ }
+
+ randomClassNameFromRange(range)
+ {
+ const maximum = Math.round(range * this.configuration.classCount);
+ return `class${ this.random.numberSquareWeightedToLow(maximum) }`;
+ }
+
+ randomAttributeName()
+ {
+ const attributeCount = this.configuration.attributeCount;
+ return `attr${ this.random.numberSquareWeightedToLow(attributeCount) }`;
+ }
+
+ randomAttributeValue()
+ {
+ const attributeValueCount = this.configuration.attributeValueCount;
+ const valueNum = this.random.numberSquareWeightedToLow(attributeValueCount);
+ if (valueNum == 0)
+ return "";
+ if (valueNum == 1)
+ return "val";
+ return `val${valueNum}`;
+ }
+
+ randomCombinator()
+ {
+ const combinators = this.configuration.combinators;
+ return combinators[this.random.number(combinators.length)]
+ }
+
+ randomPseudoClass(isLast)
+ {
+ const pseudoClasses = this.configuration.pseudoClasses;
+ const pseudoClass = pseudoClasses[this.random.number(pseudoClasses.length)]
+ if (!isLast && pseudoClass == 'empty')
+ return this.randomPseudoClass(isLast);
+ return pseudoClass;
+ }
+
+ randomId()
+ {
+ const idCount = this.configuration.idChance * this.configuration.elementCount ;
+ return `id${ this.random.number(idCount) }`;
+ }
+
+ randomAttributeSelector()
+ {
+ const name = this.randomAttributeName();
+ const operators = this.configuration.attributeOperators;
+ const operator = operators[this.random.numberSquareWeightedToLow(operators.length)];
+ if (operator == '')
+ return `[${name}]`;
+ const value = this.randomAttributeValue();
+ return `[${name}${operator}"${value}"]`;
+ }
+
+ makeCompoundSelector(index, length)
+ {
+ const isFirst = index == 0;
+ const isLast = index == length - 1;
+ const usePseudoClass = this.random.chance(this.configuration.pseudoClassChance) && this.configuration.pseudoClasses.length;
+ const useId = isFirst && this.random.chance(this.configuration.idChance);
+ const useElement = !useId && (usePseudoClass || this.random.chance(this.configuration.elementChance)); // :nth-of-type etc only make sense with element
+ const useAttribute = !useId && this.random.chance(this.configuration.attributeChance);
+ const useIdElementOrAttribute = useId || useElement || useAttribute;
+ const useStar = !useIdElementOrAttribute && !isFirst && this.random.chance(this.configuration.starChance);
+ const useClass = !useId && !useStar && (!useIdElementOrAttribute || this.random.chance(this.configuration.classChance));
+ const useBeforeOrAfter = isLast && this.random.chance(this.configuration.beforeAfterChance);
+ let result = "";
+ if (useElement)
+ result += this.randomElementName();
+ if (useStar)
+ result = "*";
+ if (useId)
+ result += "#" + this.randomId();
+ if (useClass) {
+ const classCount = this.random.numberSquareWeightedToLow(2) + 1;
+ for (let i = 0; i < classCount; ++i) {
+ // Use a smaller pool of class names on the left side of the selectors to create containers.
+ result += "." + this.randomClassNameFromRange((index + 1) / length);
+ }
+ }
+ if (useAttribute)
+ result += this.randomAttributeSelector();
+
+ if (usePseudoClass)
+ result += ":" + this.randomPseudoClass(isLast);
+ if (useBeforeOrAfter) {
+ if (this.random.chance(0.5))
+ result += "::before";
+ else
+ result += "::after";
+ }
+ return result;
+ }
+
+ makeSelector()
+ {
+ const length = this.random.number(this.configuration.maximumSelectorLength) + 1;
+ let result = this.makeCompoundSelector(0, length);
+ for (let i = 1; i < length; ++i) {
+ const combinator = this.randomCombinator();
+ if (combinator != ' ')
+ result += " " + combinator;
+ result += " " + this.makeCompoundSelector(i, length);
+ }
+ return result;
+ }
+
+ get randomColorComponent()
+ {
+ return this.random.next % 256;
+ }
+
+ makeDeclaration(selector)
+ {
+ let declaration = `background-color: rgb(${this.randomColorComponent}, ${this.randomColorComponent}, ${this.randomColorComponent});`;
+
+ if (selector.endsWith('::before') || selector.endsWith('::after'))
+ declaration += " content: ''; min-width:5px; display:inline-block;";
+
+ return declaration;
+ }
+
+ makeRule()
+ {
+ const selector = this.makeSelector();
+ return selector + " { " + this.makeDeclaration(selector) + " }";
+ }
+
+ makeStylesheet(size)
+ {
+ let cssText = "";
+ for (let i = 0; i < size; ++i)
+ cssText += this.makeRule() + "\n";
+ return cssText;
+ }
+
+ makeStyle()
+ {
+ this.testStyle = document.createElement("style");
+ this.testStyle.textContent = this.makeStylesheet(this.configuration.ruleCount);
+
+ document.head.appendChild(this.testStyle);
+ }
+
+ makeElement()
+ {
+ const element = document.createElement(this.randomElementName());
+ const hasClasses = this.random.chance(this.configuration.elementClassChance);
+ const hasAttributes = this.random.chance(this.configuration.elementAttributeChance);
+ if (hasClasses) {
+ const count = this.random.numberSquareWeightedToLow(this.configuration.elementMaximumClasses) + 1;
+ for (let i = 0; i < count; ++i)
+ element.classList.add(this.randomClassName());
+ }
+ if (hasAttributes) {
+ const count = this.random.number(this.configuration.elementMaximumAttributes) + 1;
+ for (let i = 0; i < count; ++i)
+ element.setAttribute(this.randomAttributeName(), this.randomAttributeValue());
+ }
+ const hasId = this.random.chance(this.configuration.idChance);
+ if (hasId) {
+ element.id = `id${ this.idCount }`;
+ this.idCount++;
+ }
+ return element;
+ }
+
+ makeTreeWithDepth(parent, remainingCount, depth)
+ {
+ const maximumDepth = this.configuration.maximumTreeDepth;
+ const maximumWidth = this.configuration.maximumTreeWidth;
+ const nonEmptyChance = (maximumDepth - depth) / maximumDepth;
+
+ const shouldRepeat = this.random.chance(this.configuration.repeatingSequenceChance);
+ const repeatingSequenceLength = shouldRepeat ? this.random.number(this.configuration.repeatingSequenceMaximumLength) + 1 : 0;
+
+ let childCount = 0;
+ if (depth == 0)
+ childCount = remainingCount;
+ else if (this.random.chance(nonEmptyChance))
+ childCount = this.random.number(maximumWidth * depth / maximumDepth);
+
+ let repeatingSequence = [];
+ let repeatingSequenceSize = 0;
+ for (let i = 0; i < childCount; ++i) {
+ if (shouldRepeat && repeatingSequence.length == repeatingSequenceLength && repeatingSequenceSize < remainingCount) {
+ for (const subtree of repeatingSequence)
+ parent.appendChild(subtree.cloneNode(true));
+ remainingCount -= repeatingSequenceSize;
+ if (!remainingCount)
+ return 0;
+ continue;
+ }
+ const element = this.makeElement();
+ parent.appendChild(element);
+
+ if (!--remainingCount)
+ return 0;
+ remainingCount = this.makeTreeWithDepth(element, remainingCount, depth + 1);
+ if (!remainingCount)
+ return 0;
+
+ if (shouldRepeat && repeatingSequence.length < repeatingSequenceLength) {
+ repeatingSequence.push(element);
+ repeatingSequenceSize += element.querySelectorAll("*").length + 1;
+ }
+ }
+ return remainingCount;
+ }
+
+ makeTree()
+ {
+ this.testRoot = document.querySelector("#testroot");
+ const elementCount = this.configuration.elementCount;
+
+ this.makeTreeWithDepth(this.testRoot, elementCount, 0);
+
+ this.updateCachedTestElements();
+ }
+
+ updateCachedTestElements()
+ {
+ this.testElements = this.testRoot.querySelectorAll("*");
+ }
+
+ randomTreeElement()
+ {
+ const randomIndex = this.random.number(this.testElements.length);
+ return this.testElements[randomIndex]
+ }
+
+ addClasses(count)
+ {
+ for (let i = 0; i < count;) {
+ const element = this.randomTreeElement();
+ // There are more leaves than branches. Avoid skewing towards leaf mutations.
+ if (!element.firstChild && !this.random.chance(this.configuration.leafMutationChance))
+ continue;
+ ++i;
+ const classList = element.classList;
+ classList.add(this.randomClassName());
+ }
+ }
+
+ removeClasses(count)
+ {
+ for (let i = 0; i < count;) {
+ const element = this.randomTreeElement();
+ const classList = element.classList;
+ if (!element.firstChild && !this.random.chance(this.configuration.leafMutationChance))
+ continue;
+ if (!classList.length)
+ continue;
+ ++i;
+ classList.remove(classList[0]);
+ }
+ }
+
+ addLeafElements(count)
+ {
+ for (let i = 0; i < count;) {
+ const parent = this.randomTreeElement();
+ // Avoid altering tree shape by turning many leaves into containers.
+ if (!parent.firstChild)
+ continue;
+ ++i;
+ const children = parent.childNodes;
+ const index = this.random.number(children.length + 1);
+ parent.insertBefore(this.makeElement(), children[index]);
+ }
+ this.updateCachedTestElements();
+ }
+
+ removeLeafElements(count)
+ {
+ for (let i = 0; i < count;) {
+ const element = this.randomTreeElement();
+
+ const canRemove = !element.firstChild && element.parentNode;
+ if (!canRemove)
+ continue;
+ ++i;
+ element.parentNode.removeChild(element);
+ }
+ this.updateCachedTestElements();
+ }
+
+ mutateAttributes(count)
+ {
+ for (let i = 0; i < count;) {
+ const element = this.randomTreeElement();
+ // There are more leaves than branches. Avoid skewing towards leaf mutations.
+ if (!element.firstChild && !this.random.chance(this.configuration.leafMutationChance))
+ continue;
+ const attributeNames = element.getAttributeNames();
+ let mutatedAttributes = false;
+ for (const name of attributeNames) {
+ if (name == "class" || name == "id")
+ continue;
+ if (this.random.chance(0.5))
+ element.removeAttribute(name);
+ else
+ element.setAttribute(name, this.randomAttributeValue());
+ mutatedAttributes = true;
+ }
+ if (!mutatedAttributes) {
+ const attributeCount = this.random.number(this.configuration.elementMaximumAttributes) + 1;
+ for (let j = 0; j < attributeCount; ++j)
+ element.setAttribute(this.randomAttributeName(), this.randomAttributeValue());
+ }
+ ++i;
+ }
+ }
+
+ async runForever()
+ {
+ while (true) {
+ this.addClasses(10);
+ this.removeClasses(10);
+ this.addLeafElements(10);
+ this.removeLeafElements(10);
+ this.mutateAttributes(10);
+
+ await nextAnimationFrame();
+ }
+ }
+}
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/tests.js b/third_party/webkit/PerformanceTests/StyleBench/resources/tests.js
new file mode 100644
index 0000000000..85778316fa
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/StyleBench/resources/tests.js
@@ -0,0 +1,40 @@
+function makeSteps(configuration)
+{
+ const steps = [];
+ for (i = 0; i < configuration.stepCount; ++i) {
+ steps.push(new BenchmarkTestStep(`Adding classes - ${i}`, (bench, contentWindow, contentDocument) => {
+ bench.addClasses(configuration.mutationsPerStep);
+ }));
+ steps.push(new BenchmarkTestStep(`Removing classes - ${i}`, (bench, contentWindow, contentDocument) => {
+ bench.removeClasses(configuration.mutationsPerStep);
+ }));
+ steps.push(new BenchmarkTestStep(`Mutating attributes - ${i}`, (bench, contentWindow, contentDocument) => {
+ bench.mutateAttributes(configuration.mutationsPerStep);
+ }));
+ steps.push(new BenchmarkTestStep(`Adding leaf elements - ${i}`, (bench, contentWindow, contentDocument) => {
+ bench.addLeafElements(configuration.mutationsPerStep);
+ }));
+ steps.push(new BenchmarkTestStep(`Removing leaf elements - ${i}`, (bench, contentWindow, contentDocument) => {
+ bench.removeLeafElements(configuration.mutationsPerStep);
+ }));
+ }
+ return steps;
+}
+
+function makeSuite(configuration)
+{
+ return {
+ name: configuration.name,
+ url: 'style-bench.html',
+ prepare: (runner, contentWindow, contentDocument) => {
+ return runner.waitForElement('#testroot').then((element) => {
+ return contentWindow.createBenchmark(configuration);
+ });
+ },
+ tests: makeSteps(configuration),
+ };
+}
+
+var Suites = [];
+for (const configuration of StyleBench.predefinedConfigurations())
+ Suites.push(makeSuite(configuration));