summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js')
-rw-r--r--toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js276
1 files changed, 276 insertions, 0 deletions
diff --git a/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js
new file mode 100644
index 000000000..c64a80a05
--- /dev/null
+++ b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js
@@ -0,0 +1,276 @@
+/* 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/. */
+
+"use strict";
+
+var trace;
+var service;
+var reports;
+
+function onLoad() {
+ trace = document.getElementById('trace');
+ service = new CheckerboardReportService();
+ updateEnabled();
+ reports = service.getReports();
+ for (var i = 0; i < reports.length; i++) {
+ let text = "Severity " + reports[i].severity + " at " + new Date(reports[i].timestamp).toString();
+ let link = document.createElement('a');
+ link.href = 'javascript:showReport(' + i + ')';
+ link.textContent = text;
+ let bullet = document.createElement('li');
+ bullet.appendChild(link);
+ document.getElementById(reports[i].reason).appendChild(bullet);
+ }
+}
+
+function updateEnabled() {
+ let enabled = document.getElementById('enabled');
+ if (service.isRecordingEnabled()) {
+ enabled.textContent = 'enabled';
+ enabled.style.color = 'green';
+ } else {
+ enabled.textContent = 'disabled';
+ enabled.style.color = 'red';
+ }
+}
+
+function toggleEnabled() {
+ service.setRecordingEnabled(!service.isRecordingEnabled());
+ updateEnabled();
+}
+
+function flushReports() {
+ service.flushActiveReports();
+}
+
+function showReport(index) {
+ trace.value = reports[index].log;
+ loadData();
+}
+
+// -- Code to load and render the trace --
+
+const CANVAS_USE_RATIO = 0.75;
+const FRAME_INTERVAL_MS = 50;
+const VECTOR_NORMALIZED_MAGNITUDE = 30.0;
+
+var renderData = new Array();
+var currentFrame = 0;
+var playing = false;
+var timerId = 0;
+
+var minX = undefined;
+var minY = undefined;
+var maxX = undefined;
+var maxY = undefined;
+
+function log(x) {
+ if (console) {
+ console.log(x);
+ }
+}
+
+function getFlag(flag) {
+ return document.getElementById(flag).checked;
+}
+
+// parses the lines in the textarea, ignoring anything that doesn't have RENDERTRACE.
+// for each matching line, tokenizes on whitespace and ignores all tokens prior to
+// RENDERTRACE. Additional info can be included at the end of the line, and will be
+// displayed but not parsed. Allowed syntaxes:
+// <junk> RENDERTRACE <timestamp> rect <color> <x> <y> <width> <height> [extraInfo]
+function loadData() {
+ stopPlay();
+ renderData = new Array();
+ currentFrame = 0;
+ minX = undefined;
+ minY = undefined;
+ maxX = undefined;
+ maxY = undefined;
+
+ var charPos = 0;
+ var lastLineLength = 0;
+ var lines = trace.value.split(/\r|\n/);
+ for (var i = 0; i < lines.length; i++) {
+ charPos += lastLineLength;
+ lastLineLength = lines[i].length + 1;
+ // skip lines without RENDERTRACE
+ if (! /RENDERTRACE/.test(lines[i])) {
+ continue;
+ }
+
+ var tokens = lines[i].split(/\s+/);
+ var j = 0;
+ // skip tokens until RENDERTRACE
+ while (j < tokens.length && tokens[j++] != "RENDERTRACE"); // empty loop body
+ if (j >= tokens.length - 2) {
+ log("Error parsing line: " + lines[i]);
+ continue;
+ }
+
+ var timestamp = tokens[j++];
+ var destIndex = renderData.length;
+ if (destIndex == 0) {
+ // create the initial frame
+ renderData.push({
+ timestamp: timestamp,
+ rects: {},
+ });
+ } else if (renderData[destIndex - 1].timestamp == timestamp) {
+ // timestamp hasn't changed use, so update the previous object
+ destIndex--;
+ } else {
+ // clone a new copy of the last frame and update timestamp
+ renderData.push(JSON.parse(JSON.stringify(renderData[destIndex - 1])));
+ renderData[destIndex].timestamp = timestamp;
+ }
+
+ switch (tokens[j++]) {
+ case "rect":
+ if (j > tokens.length - 5) {
+ log("Error parsing line: " + lines[i]);
+ continue;
+ }
+
+ var rect = {};
+ var color = tokens[j++];
+ renderData[destIndex].rects[color] = rect;
+ rect.x = parseFloat(tokens[j++]);
+ rect.y = parseFloat(tokens[j++]);
+ rect.width = parseFloat(tokens[j++]);
+ rect.height = parseFloat(tokens[j++]);
+ rect.dataText = trace.value.substring(charPos, charPos + lines[i].length);
+
+ if (!getFlag('excludePageFromZoom') || color != 'brown') {
+ if (typeof minX == "undefined") {
+ minX = rect.x;
+ minY = rect.y;
+ maxX = rect.x + rect.width;
+ maxY = rect.y + rect.height;
+ } else {
+ minX = Math.min(minX, rect.x);
+ minY = Math.min(minY, rect.y);
+ maxX = Math.max(maxX, rect.x + rect.width);
+ maxY = Math.max(maxY, rect.y + rect.height);
+ }
+ }
+ break;
+
+ default:
+ log("Error parsing line " + lines[i]);
+ break;
+ }
+ }
+
+ if (! renderFrame()) {
+ alert("No data found; nothing to render!");
+ }
+}
+
+// render the current frame (i.e. renderData[currentFrame])
+// returns false if currentFrame is out of bounds, true otherwise
+function renderFrame() {
+ var frame = currentFrame;
+ if (frame < 0 || frame >= renderData.length) {
+ log("Invalid frame index");
+ return false;
+ }
+
+ var canvas = document.getElementById('canvas');
+ if (! canvas.getContext) {
+ log("No canvas context");
+ }
+
+ var context = canvas.getContext('2d');
+
+ // midpoint of the bounding box
+ var midX = (minX + maxX) / 2.0;
+ var midY = (minY + maxY) / 2.0;
+
+ // midpoint of the canvas
+ var cmx = canvas.width / 2.0;
+ var cmy = canvas.height / 2.0;
+
+ // scale factor
+ var scale = CANVAS_USE_RATIO * Math.min(canvas.width / (maxX - minX), canvas.height / (maxY - minY));
+
+ function projectX(value) {
+ return cmx + ((value - midX) * scale);
+ }
+
+ function projectY(value) {
+ return cmy + ((value - midY) * scale);
+ }
+
+ function drawRect(color, rect) {
+ context.strokeStyle = color;
+ context.strokeRect(
+ projectX(rect.x),
+ projectY(rect.y),
+ rect.width * scale,
+ rect.height * scale);
+ }
+
+ // clear canvas
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ var activeData = '';
+ // draw rects
+ for (var i in renderData[frame].rects) {
+ drawRect(i, renderData[frame].rects[i]);
+ activeData += "\n" + renderData[frame].rects[i].dataText;
+ }
+ // draw timestamp and frame counter
+ context.fillStyle = 'black';
+ context.fillText((frame + 1) + "/" + renderData.length + ": " + renderData[frame].timestamp, 5, 15);
+
+ document.getElementById('active').textContent = activeData;
+
+ return true;
+}
+
+// -- Player controls --
+
+function reset(beginning) {
+ currentFrame = (beginning ? 0 : renderData.length - 1);
+ renderFrame();
+}
+
+function step(backwards) {
+ if (playing) {
+ togglePlay();
+ }
+ currentFrame += (backwards ? -1 : 1);
+ if (! renderFrame()) {
+ currentFrame -= (backwards ? -1 : 1);
+ }
+}
+
+function pause() {
+ clearInterval(timerId);
+ playing = false;
+}
+
+function togglePlay() {
+ if (playing) {
+ pause();
+ } else {
+ timerId = setInterval(function() {
+ currentFrame++;
+ if (! renderFrame()) {
+ currentFrame--;
+ togglePlay();
+ }
+ }, FRAME_INTERVAL_MS);
+ playing = true;
+ }
+}
+
+function stopPlay() {
+ if (playing) {
+ togglePlay();
+ }
+ currentFrame = 0;
+ renderFrame();
+}