Eliminate a request for layout every time an item is added to the stage of the graphi...
[WebKit-https.git] / PerformanceTests / Animometer / resources / extensions.js
1 function Point(x, y)
2 {
3     this.x = x;
4     this.y = y;
5 }
6
7 Point.pointOnCircle = function(angle, radius)
8 {
9     return new Point(radius * Math.cos(angle), radius * Math.sin(angle));
10 }
11
12 Point.pointOnEllipse = function(angle, radiuses)
13 {
14     return new Point(radiuses.x * Math.cos(angle), radiuses.y * Math.sin(angle));
15 }
16
17 Point.elementClientSize = function(element)
18 {
19     return new Point(element.clientWidth, element.clientHeight);
20 }
21
22 Point.prototype =
23 {
24     // Used when the point object is used as a size object.
25     get width()
26     {
27         return this.x;
28     },
29     
30     // Used when the point object is used as a size object.
31     get height()
32     {
33         return this.y;
34     },
35     
36     // Used when the point object is used as a size object.
37     get center()
38     {
39         return new Point(this.x / 2, this.y / 2);
40     },
41     
42     add: function(other)
43     {
44         return new Point(this.x + other.x, this.y + other.y);
45     },
46     
47     subtract: function(other)
48     {
49         return new Point(this.x - other.x, this.y - other.y);
50     },
51     
52     multiply: function(other)
53     {
54         return new Point(this.x * other.x, this.y * other.y);
55     },
56     
57     move: function(angle, velocity, timeDelta)
58     {
59         return this.add(Point.pointOnCircle(angle, velocity * (timeDelta / 1000)));
60     }
61 }
62
63 function Insets(top, right, bottom, left)
64 {
65     this.top = top;
66     this.right = right;
67     this.bottom = bottom;
68     this.left = left;
69 }
70
71 Insets.elementPadding = function(element)
72 {
73     var styles = window.getComputedStyle(element);
74     return new Insets(
75         parseFloat(styles.paddingTop),
76         parseFloat(styles.paddingRight),
77         parseFloat(styles.paddingBottom),
78         parseFloat(styles.paddingTop));
79 }
80
81 Insets.prototype =
82 {
83     get width()
84     {
85         return this.left + this.right;
86     },
87
88     get height()
89     {
90         return this.top + this.bottom;
91     },
92     
93     get size()
94     {
95         return new Point(this.width, this.height);
96     }
97 }
98
99 function SimplePromise()
100 {
101     this._chainedPromise = null;
102     this._callback = null;
103 }
104
105 SimplePromise.prototype.then = function (callback)
106 {
107     if (this._callback)
108         throw "SimplePromise doesn't support multiple calls to then";
109         
110     this._callback = callback;
111     this._chainedPromise = new SimplePromise;
112     
113     if (this._resolved)
114         this.resolve(this._resolvedValue);
115
116     return this._chainedPromise;
117 }
118
119 SimplePromise.prototype.resolve = function (value)
120 {
121     if (!this._callback) {
122         this._resolved = true;
123         this._resolvedValue = value;
124         return;
125     }
126
127     var result = this._callback(value);
128     if (result instanceof SimplePromise) {
129         var chainedPromise = this._chainedPromise;
130         result.then(function (result) { chainedPromise.resolve(result); });
131     } else
132         this._chainedPromise.resolve(result);
133 }
134
135 window.DocumentExtension =
136 {
137     createElement : function(name, attrs, parentElement)
138     {
139         var element = document.createElement(name);
140
141         for (var key in attrs)
142             element.setAttribute(key, attrs[key]);
143
144         parentElement.appendChild(element);
145         return element;
146     },
147
148     createSvgElement: function(name, attrs, xlinkAttrs, parentElement)
149     {
150         const svgNamespace = "http://www.w3.org/2000/svg";
151         const xlinkNamespace = "http://www.w3.org/1999/xlink";
152
153         var element = document.createElementNS(svgNamespace, name);
154         
155         for (var key in attrs)
156             element.setAttribute(key, attrs[key]);
157             
158         for (var key in xlinkAttrs)
159             element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
160             
161         parentElement.appendChild(element);
162         return element;
163     },
164     
165     insertCssRuleAfter: function(newRule, referenceRule)
166     {
167         var styleSheets = document.styleSheets;
168
169         for (var i = 0; i < styleSheets.length; ++i) {       
170             for (var j = 0; j < styleSheets[i].cssRules.length; ++j) {
171                 if (styleSheets[i].cssRules[j].selectorText == referenceRule) {
172                     styleSheets[i].insertRule(newRule, j + 1);
173                     return true;
174                 }
175             }
176         }
177         
178         return false;
179     }
180 }
181
182 function ProgressBar(element, ranges)
183 {
184     this.element = element;
185     this.ranges = ranges;
186     this.currentRange = 0;
187 }
188
189 ProgressBar.prototype =
190 {
191     _progressToPercent: function(progress)
192     {
193         return progress * (100 / this.ranges);
194     },
195     
196     incRange: function()
197     {
198         ++this.currentRange;
199     },
200     
201     setPos: function(progress)
202     {
203         this.element.style.width = this._progressToPercent(this.currentRange + progress) + "%";
204     }
205 }
206
207 function ResultsDashboard()
208 {
209     this._iterationsSamplers = [];
210 }
211
212 ResultsDashboard.prototype =
213 {
214     push: function(suitesSamplers)
215     {
216         this._iterationsSamplers.push(suitesSamplers);        
217     },
218     
219     toJSON: function(statistics, graph)
220     {
221         var iterationsResults = [];
222         var iterationsScores = [];
223         
224         this._iterationsSamplers.forEach(function(iterationSamplers, index) {
225             var suitesResults = {};
226             var suitesScores = [];
227         
228             for (var suiteName in iterationSamplers) {
229                 var suite = suiteFromName(suiteName);
230                 var suiteSamplers = iterationSamplers[suiteName];
231
232                 var testsResults = {};
233                 var testsScores = [];
234                 
235                 for (var testName in suiteSamplers) {
236                     var sampler = suiteSamplers[testName];
237                     testsResults[testName] = sampler.toJSON(statistics, graph);
238                     testsScores.push(testsResults[testName][Strings["JSON_SCORE"]]);
239                 }
240
241                 suitesResults[suiteName] =  {};
242                 suitesResults[suiteName][Strings["JSON_SCORE"]] = Statistics.geometricMean(testsScores);
243                 suitesResults[suiteName][Strings["JSON_RESULTS"][2]] = testsResults;
244                 suitesScores.push(suitesResults[suiteName][Strings["JSON_SCORE"]]);
245             }
246             
247             iterationsResults[index] = {};
248             iterationsResults[index][Strings["JSON_SCORE"]] = Statistics.geometricMean(suitesScores);
249             iterationsResults[index][Strings["JSON_RESULTS"][1]] = suitesResults;
250             iterationsScores.push(iterationsResults[index][Strings["JSON_SCORE"]]);
251         });
252
253         var json = {};
254         json[Strings["JSON_SCORE"]] = Statistics.sampleMean(iterationsScores.length, iterationsScores.reduce(function(a, b) { return a * b; }));
255         json[Strings["JSON_RESULTS"][0]] = iterationsResults;
256         return json;
257     }
258 }
259
260 function ResultsTable(element, headers)
261 {
262     this.element = element;
263     this._headers = headers;
264     this.clear();
265 }
266
267 ResultsTable.prototype =
268 {
269     clear: function()
270     {
271         this.element.innerHTML = "";
272     },
273
274     _showHeaderRow: function(row, queue, headers, message)
275     {
276         headers.forEach(function (header) {
277             var th = document.createElement("th");
278             th.textContent = header.text;
279             if (typeof message != "undefined" && message.length) {
280                 th.appendChild(document.createElement('br'));
281                 th.appendChild(document.createTextNode('[' + message +']'));
282                 message = "";
283             }
284             if ("width" in header)
285                 th.width = header.width + "%";
286             row.appendChild(th);
287             queue.push({element: th, headers: header.children });
288         });
289     },
290
291     _showHeader: function(message)
292     {
293         var thead = DocumentExtension.createElement("thead", {}, this.element);
294         var row = DocumentExtension.createElement("tr", {}, thead);
295
296         var queue = [];
297         this._showHeaderRow(row, queue, this._headers, message);
298
299         while (queue.length) {
300             var row = null;
301             var entries = [];
302
303             for (var i = 0, len = queue.length; i < len; ++i) {
304                 var entry = queue.shift();
305
306                 if (!entry.headers.length) {
307                     entries.push(entry.element);
308                     continue;
309                 }
310
311                 if (!row)
312                     row = DocumentExtension.createElement("tr", {}, thead);
313
314                 this._showHeaderRow(row, queue, entry.headers, "");
315                 entry.element.colSpan = entry.headers.length;
316             }
317
318             if (row) {
319                 entries.forEach(function(entry) {
320                     ++entry.rowSpan;
321                 });
322             }
323         }
324     },
325     
326     _showEmpty: function(row)
327     {
328         var td = document.createElement("td");
329         row.appendChild(td);
330     },
331
332     _showText: function(row, text)
333     {
334         var td = document.createElement("td");
335         td.textContent = text;
336         row.appendChild(td);
337     },
338
339     _showFixedNumber: function(row, value, digits)
340     {
341         var td = document.createElement("td");
342         td.textContent = value.toFixed(digits || 2);
343         row.appendChild(td);
344     },
345     
346     _showGraph: function(row, testName, testResults)
347     {
348         var data = testResults[Strings["JSON_SAMPLES"][0]];
349         if (!data) {
350             this._showEmpty(row);
351             return;
352         }
353         
354         var td = document.createElement("td");
355         var button = document.createElement("button");
356         button.className = "small-button";
357
358         button.addEventListener("click", function() {
359             var samples = data[Strings["JSON_GRAPH"][0]];
360             var samplingTimeOffset = data[Strings["JSON_GRAPH"][1]];
361             var axes = Strings["TEXT_EXPERIMENTS"];
362             benchmarkController.showTestGraph(testName, axes, samples, samplingTimeOffset);
363         });
364             
365         button.textContent = Strings["TEXT_RESULTS"][1] + "...";
366         td.appendChild(button);
367         row.appendChild(td);
368     },
369
370     _showJSON: function(row, testName, testResults)
371     {
372         var data = testResults[Strings["JSON_SAMPLES"][0]];
373         if (!data) {
374             this._showEmpty(row);
375             return;
376         }
377         
378         var td = document.createElement("td");
379         var button = document.createElement("button");
380         button.className = "small-button";
381
382         button.addEventListener("click", function() {
383             benchmarkController.showTestJSON(testName, testResults);
384         });
385             
386         button.textContent = Strings["TEXT_RESULTS"][2] + "...";
387         td.appendChild(button);
388         row.appendChild(td);
389     },
390
391     _showTest: function(testName, testResults)
392     {
393         var row = document.createElement("tr");
394         
395         for (var index = 0; index < this._headers.length; ++index) {
396
397             switch (index) {
398             case 0:
399                 this._showText(row, testName);
400                 break;
401
402             case 1:
403                 var data = testResults[Strings["JSON_SCORE"][0]];
404                 this._showFixedNumber(row, data, 2);
405                 break;
406
407             case 2:
408             case 3:
409                 var data = testResults[Strings["JSON_EXPERIMENTS"][index - 2]];
410                 for (var measurement in data)
411                     this._showFixedNumber(row, data[measurement], 2);
412                 break;
413                 
414             case 4:
415                 this._showGraph(row, testName, testResults);
416                 this._showJSON(row, testName, testResults);
417                 break;
418             }
419         }
420         
421         this.element.appendChild(row);
422     },
423
424     _showSuite: function(suiteName, suiteResults)
425     {
426         for (var testName in suiteResults[Strings["JSON_RESULTS"][2]]) {
427             this._showTest(testName, suiteResults[Strings["JSON_RESULTS"][2]][testName]);
428         }
429     },
430     
431     _showIteration : function(iterationResults)
432     {
433         for (var suiteName in iterationResults[Strings["JSON_RESULTS"][1]]) {
434             this._showSuite(suiteName, iterationResults[Strings["JSON_RESULTS"][1]][suiteName]);
435         }
436     },
437     
438     showRecord: function(testName, message, testResults)
439     {
440         this.clear();
441         this._showHeader(message);
442         this._showTest(testName, testResults);
443     },
444
445     showIterations: function(iterationsResults)
446     {
447         this.clear();
448         this._showHeader("");
449         
450         iterationsResults.forEach(function(iterationResults) {
451             this._showIteration(iterationResults);
452         }, this);
453     }
454 }
455