diff options
Diffstat (limited to 'toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js')
-rw-r--r-- | toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js new file mode 100644 index 0000000000..e985a312ad --- /dev/null +++ b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js @@ -0,0 +1,324 @@ +/* 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 (let 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 = "#"; + link.addEventListener("click", function () { + showReport(i); + return false; + }); + 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"); + let isEnabled = service.isRecordingEnabled(); + if (isEnabled) { + enabled.textContent = "enabled"; + } else { + enabled.textContent = "disabled"; + } + enabled.classList.toggle("enabled", isEnabled); +} + +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 = []; +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 = []; + 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 + // eslint-disable-next-line no-empty + 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, + 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(); +} + +document.getElementById("pauseButton").addEventListener("click", togglePlay); +document.getElementById("stopButton").addEventListener("click", stopPlay); +document + .getElementById("enableToggleButton") + .addEventListener("click", toggleEnabled); +document + .getElementById("flushReportsButton") + .addEventListener("click", flushReports); +document + .getElementById("excludePageFromZoom") + .addEventListener("click", loadData); +document + .getElementById("stepForwardButton") + .addEventListener("click", function () { + step(false); + }); +document.getElementById("forwardButton").addEventListener("click", function () { + reset(false); +}); +document.getElementById("rewindButton").addEventListener("click", function () { + reset(true); +}); +document + .getElementById("stepBackButton") + .addEventListener("click", function () { + step(true); + }); +window.addEventListener("load", onLoad); |