Bug 1387125 - Create a performance test to compare XBL with Custom Elements in XUL Documents;r=mrbkap
MozReview-Commit-ID: FXhDUc9LfiE
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -70,8 +70,15 @@ support-files = ../file_bug357450.js
[test_nsITextInputProcessor.xul]
[test_range_getClientRectsAndTexts.html]
[test_title.xul]
support-files = file_title.xul
[test_windowroot.xul]
[test_swapFrameLoaders.xul]
[test_groupedSHistory.xul]
[test_bug1339722.html]
+[test_perf_xbl_vs_customelement.xul]
+skip-if = true # only run this when manually invoked
+support-files =
+ file_perf_customelement.html
+ file_perf_customelement.xul
+ file_perf_xbl.xul
+ file_perf_xbl_bindings.xml
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_customelement.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-autonomous-example
+
+ class TestCustomElement extends HTMLElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ // Not including any content since with XBL all content is anonymous, and we want
+ // an apples to apples comparison. When we have access to Shadow DOM we can set up a
+ // comparison with content.
+ }
+ }
+
+ customElements.define("test-custom-element", TestCustomElement);
+ </script>
+ <script type="application/javascript">
+ const openerWin = opener.wrappedJSObject;
+ const ok = openerWin.ok.bind(openerWin);
+ const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+ const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+ function run() {
+ let container = document.getElementById("element-container");
+ let iterationTimes = [];
+
+ for (var i = 0; i < NUM_ITERATIONS; i++) {
+ const measure = openerWin.startMeasure("custom elements");
+ for (var j = 0; j < NUM_ELEMENTS; j++) {
+ var element = document.createElement("test-custom-element");
+ container.appendChild(element);
+ }
+ const {duration} = measure.stop();
+ iterationTimes.push(duration);
+ container.innerHTML = '';
+ }
+
+ openerWin.customElementHTMLDone(iterationTimes);
+ }
+ </script>
+ <body onload="run()">
+ <div id="element-container"></div>
+ </body>
+</head>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_customelement.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="run()">
+ <label value="Mozilla Bug 1209621"/>
+ <script type="application/javascript">
+ // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-autonomous-example
+
+ class TestCustomElement extends XULElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ // Not including any content since with XBL all content is anonymous, and we want
+ // an apples to apples comparison. When we have access to Shadow DOM we can set up a
+ // comparison with content.
+ }
+ }
+
+ customElements.define("test-custom-element", TestCustomElement);
+ </script>
+ <script type="application/javascript"><![CDATA[
+ const openerWin = opener.wrappedJSObject;
+ const ok = openerWin.ok.bind(openerWin);
+ const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+ const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+ function run() {
+ let container = document.getElementById("element-container");
+ let iterationTimes = [];
+
+ for (var i = 0; i < NUM_ITERATIONS; i++) {
+ const measure = openerWin.startMeasure("custom elements");
+ for (var j = 0; j < NUM_ELEMENTS; j++) {
+ var element = document.createElement("test-custom-element");
+ container.appendChild(element);
+ }
+ const {duration} = measure.stop();
+ iterationTimes.push(duration);
+ container.innerHTML = '';
+ }
+
+ openerWin.customElementXULDone(iterationTimes);
+ }
+ ]]></script>
+ <box id="element-container"></box>
+</window>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_xbl.xul
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="run()">
+ <label value="Mozilla Bug 1209621"/>
+ <style xmlns="http://www.w3.org/1999/xhtml">
+ foo {
+ -moz-binding: url("file_perf_xbl_bindings.xml#test");
+ }
+ </style>
+ <script type="application/javascript"><![CDATA[
+ const openerWin = opener.wrappedJSObject;
+ const ok = openerWin.ok.bind(openerWin);
+ const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+ const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+ function run() {
+ let container = document.getElementById("element-container");
+ let iterationTimes = [];
+
+ for (var i = 0; i < NUM_ITERATIONS; i++) {
+ const measure = openerWin.startMeasure("xbl elements");
+ for (var j = 0; j < NUM_ELEMENTS; j++) {
+ var element = document.createElement("foo");
+ container.appendChild(element);
+ }
+ const {duration} = measure.stop();
+ iterationTimes.push(duration);
+ container.innerHTML = '';
+ }
+
+ openerWin.xblDone(iterationTimes);
+ }
+ ]]></script>
+ <box id="element-container"></box>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_xbl_bindings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<bindings xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+ <binding id="test">
+ </binding>
+</bindings>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_perf_xbl_vs_customelement.xul
@@ -0,0 +1,155 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1387125
+-->
+<window title="Mozilla Bug 1387125"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1387125"
+ target="_blank">Mozilla Bug 1387125</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 1387125 **/
+
+ Components.utils.import("resource://gre/modules/FileUtils.jsm");
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ window.NUM_ELEMENTS = 1000;
+ window.NUM_ITERATIONS = 5;
+
+ let xblWindow;
+ let customElementXULWindow;
+ let customElementHTMLWindow;
+ let xblTimes;
+ let customElementXULTimes;
+ let customElementHTMLTimes;
+
+ SimpleTest.waitForExplicitFinish();
+
+ function startMeasure(label) {
+ const startLabel = label + "start";
+ performance.mark(startLabel);
+ return {
+ stop: (clear = true) => {
+ performance.measure(label, startLabel);
+ const entries = performance.getEntriesByName(label);
+ if (clear) {
+ performance.clearMeasures(label);
+ }
+ return entries[entries.length - 1];
+ }
+ };
+ }
+
+ function getTimes(times) {
+ times = times.sort();
+ let totalTime = times.reduce((sum, t) => sum + t);
+ let min = times[0];
+ let max = times[times.length - 1];
+ let avg = totalTime / times.length;
+ let median = times.length % 2 !== 0
+ ? times[Math.floor(times.length / 2)]
+ : (times[(times.length / 2) - 1] + times[times.length / 2]) / 2;
+
+ return {
+ avg: Math.round(avg * 100) / 100,
+ median: Math.round(median * 100) / 100,
+ min: Math.round(min * 100) / 100,
+ max: Math.round(max * 100) / 100,
+ };
+ }
+
+ function printTimes(label, times) {
+ let {avg, median, min, max} = times;
+ let avgPerElement = Math.round(avg / NUM_ELEMENTS * 1000) / 1000;
+
+ info(`${label}: On average, it took ${avg} ms to create ${NUM_ELEMENTS} elements (median ${median} ms, max ${max} ms, min ${min} ms, per element ${avgPerElement} ms)`);
+ }
+
+ function wait(time) {
+ return new Promise(resolve => setTimeout(resolve, time));
+ }
+
+ async function xblDone(times) {
+ xblTimes = getTimes(times);
+ xblWindow.close();
+ done();
+ }
+ async function customElementHTMLDone(times) {
+ customElementHTMLTimes = getTimes(times);
+ customElementHTMLWindow.close();
+
+ await wait(500);
+ customElementXULWindow = window.open("file_perf_customelement.xul", "", "chrome,width=600,height=400");
+ }
+ async function customElementXULDone(times) {
+ customElementXULTimes = getTimes(times);
+ customElementXULWindow.close();
+ await wait(500);
+ xblWindow = window.open("file_perf_xbl.xul", "", "chrome,width=600,height=400");
+ }
+ addLoadEvent(async function() {
+ await SpecialPowers.pushPrefEnv({ set: [
+ [ "dom.webcomponents.customelements.enabled", true ],
+ [ "dom.webcomponents.enabled", true ],
+ ]});
+
+ // From https://github.com/devtools-html/perf.html/blob/b73eb73df04c7df51464fa50eeadef3dc7f5d4e2/docs/gecko-profile-format.md#L21
+ const settings = {
+ entries: 100000000,
+ interval: 1,
+ features: ["js", "stackwalk", "threads", "leaf"],
+ threads: ["GeckoMain", "Compositor"]
+ };
+ Services.profiler.StartProfiler(
+ settings.entries,
+ settings.interval,
+ settings.features,
+ settings.features.length,
+ settings.threads,
+ settings.threads.length
+ );
+
+ info("Profiler has started");
+ await wait(500);
+
+ customElementHTMLWindow = window.open("file_perf_customelement.html", "", "chrome,width=600,height=400");
+ });
+
+ async function done() {
+ ok(true, "done");
+
+ printTimes("XBL", xblTimes);
+ printTimes("CUSTOM ELEMENTS (XUL)", customElementXULTimes);
+ printTimes("CUSTOM ELEMENTS (HTML)", customElementHTMLTimes);
+
+ let file = FileUtils.getFile("TmpD", [`test_perf_xbl_vs_customelement_${Date.now()}.json`]);
+ await Services.profiler.dumpProfileToFileAsync(file.path);
+ Services.profiler.StopProfiler();
+
+info(`
+
+SAVING PROFILE: ${file.path}
+
+To upload the profile, run the following commands:
+
+ gzip ${file.path}
+ curl 'https://profile-store.appspot.com/compressed-store' --compressed --data-binary @${file.path}.gz | awk '{print "Hosted at: https://perf-html.io/public/"$1}'
+
+
+`);
+
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+</window>