From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../StyleBench/InteractiveRunner.html | 174 +++++++ .../PerformanceTests/StyleBench/README_MOZILLA | 13 + .../webkit/PerformanceTests/StyleBench/index.html | 79 ++++ .../PerformanceTests/StyleBench/mozilla.patch | 86 ++++ .../StyleBench/resources/benchmark-report.js | 103 +++++ .../StyleBench/resources/benchmark-runner.js | 299 ++++++++++++ .../StyleBench/resources/gauge.png | 0 .../StyleBench/resources/gauge@2x.png | 0 .../PerformanceTests/StyleBench/resources/main.css | 286 ++++++++++++ .../PerformanceTests/StyleBench/resources/main.js | 246 ++++++++++ .../StyleBench/resources/style-bench.html | 32 ++ .../StyleBench/resources/style-bench.js | 511 +++++++++++++++++++++ .../PerformanceTests/StyleBench/resources/tests.js | 40 ++ 13 files changed, 1869 insertions(+) create mode 100644 third_party/webkit/PerformanceTests/StyleBench/InteractiveRunner.html create mode 100644 third_party/webkit/PerformanceTests/StyleBench/README_MOZILLA create mode 100644 third_party/webkit/PerformanceTests/StyleBench/index.html create mode 100644 third_party/webkit/PerformanceTests/StyleBench/mozilla.patch create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/gauge.png create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/gauge@2x.png create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/main.css create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/main.js create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.html create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/style-bench.js create mode 100644 third_party/webkit/PerformanceTests/StyleBench/resources/tests.js (limited to 'third_party/webkit/PerformanceTests/StyleBench') diff --git a/third_party/webkit/PerformanceTests/StyleBench/InteractiveRunner.html b/third_party/webkit/PerformanceTests/StyleBench/InteractiveRunner.html new file mode 100644 index 0000000000..9750dcbd5c --- /dev/null +++ b/third_party/webkit/PerformanceTests/StyleBench/InteractiveRunner.html @@ -0,0 +1,174 @@ + + + +Speedometer 2.0 Interactive Runner + + + + + + + + + diff --git a/third_party/webkit/PerformanceTests/StyleBench/README_MOZILLA b/third_party/webkit/PerformanceTests/StyleBench/README_MOZILLA new file mode 100644 index 0000000000..02677c4223 --- /dev/null +++ b/third_party/webkit/PerformanceTests/StyleBench/README_MOZILLA @@ -0,0 +1,13 @@ +The source from this directory was copied from the +PerformanceTests/StyleBench directory of the Webkit repository +at: https://svn.webkit.org/repository/webkit/trunk + +The SVN revision used was: 234578 + +The contents of this directory are intended for use to "train" the +profile guided optimization (PGO) of Firefox and for benchmarking +scenarios. The files inside this directory are not intended to ship +with Firefox or any other product. If files inside this directory +are useful for other purposes (e.g. JavaScript libraries), consumers +should vendor those files separately, as it is not appropriate to pull +in components of StyleBench for use outside of StyleBench. diff --git a/third_party/webkit/PerformanceTests/StyleBench/index.html b/third_party/webkit/PerformanceTests/StyleBench/index.html new file mode 100644 index 0000000000..83dc2345a1 --- /dev/null +++ b/third_party/webkit/PerformanceTests/StyleBench/index.html @@ -0,0 +1,79 @@ + + + + + StyleBench 0.3 + + + + + + + + + +
+ + +
+

+ StyleBench is a browser benchmark that measures the performance of the style resolution mechanism. +

+

+ Your browser window is too small. For most accurate results, please make the viewport size at least 850px by 650px.
+ It's currently . +
+

+ +
+

About StyleBench

+
+ +
+
+
+
+
+ +
+

Runs / Minute

+
+
+
+
+
+ + +
+
+ +
+

Detailed Results

+
+
+
+
+ + +
+

About StyleBench

+
+ +
+

About StyleBench

+ +

StyleBench tests performance of CSS style resolution and style invalidation. Each test run creates a large document and a large stylesheet using varying settings. It then applies a series of mutations to the document and measures the time to update the rendering. The resulting layout is simple, most of the pressure is on selector matching.

+ +

StyleBench uses Speedometer framework for UI and measurements.

+
+
+

Access via 'file:' protocol

+

To run locally, launch a web server under PerformanceTests directory with 'python -m SimpleHTTPServer 8001' and access via http://localhost:8001/StyleBench. +

+

+ Individual tests (without measurement) can also be run locally by opening PerformanceTests/StyleBench/resources/style-bench.html +

+
+
+ + diff --git a/third_party/webkit/PerformanceTests/StyleBench/mozilla.patch b/third_party/webkit/PerformanceTests/StyleBench/mozilla.patch new file mode 100644 index 0000000000..2c3988f2ff --- /dev/null +++ b/third_party/webkit/PerformanceTests/StyleBench/mozilla.patch @@ -0,0 +1,86 @@ +diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js +index b33021d9d9ce..58b3e46982d1 100644 +--- a/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js ++++ b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-report.js +@@ -1,12 +1,13 @@ + // This file can be customized to report results as needed. + + (function () { +- if (!window.testRunner && location.search != '?webkit' && location.hash != '#webkit') ++ 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); +@@ -31,20 +32,22 @@ + customIterationCount: iterationCount, + doNotIgnoreInitialRun: true, + doNotMeasureMemoryUsage: true, + continueTesting: !isLastTest, + unit: unit, + name: name, + aggregator: aggregator}; + } +- PerfTestRunner.prepareToMeasureValuesAsync(createTest(null, 'Geometric')); ++ if (window.PerfTestRunner) ++ PerfTestRunner.prepareToMeasureValuesAsync(createTest(null, 'Geometric')); + }, + didRunSuites: function (measuredValues) { +- PerfTestRunner.measureValueAsync(measuredValues.geomean); ++ 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; +@@ -63,21 +66,37 @@ + for (var subtestName in test.tests) + addToMeasuredValue(test.tests[subtestName], suiteName + '/' + testName + '/' + subtestName); + addToMeasuredValue(test.total, suiteName + '/' + testName, 'Total'); + } + addToMeasuredValue(suite.total, suiteName, 'Total'); + } + }); + +- PerfTestRunner.reportValues(createTest(null, null, false, 'pt'), scores); ++ if (window.PerfTestRunner) ++ PerfTestRunner.reportValues(createTest(null, null, false, 'pt'), scores); + + var fullNames = new Array; + for (var fullName in measuredValuesByFullName) + fullNames.push(fullName); + +- 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); ++ if (location.search == '?raptor') { ++ var data = ['raptor-benchmark', 'speedometer', measuredValuesByFullName]; ++ window.postMessage(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-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 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 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 @@ + + + +
+
+ + + +
+ + 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)); -- cgit v1.2.3