summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutcheckerboard/content
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/aboutcheckerboard/content
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/aboutcheckerboard/content')
-rw-r--r--toolkit/components/aboutcheckerboard/content/aboutCheckerboard.css56
-rw-r--r--toolkit/components/aboutcheckerboard/content/aboutCheckerboard.html79
-rw-r--r--toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js324
3 files changed, 459 insertions, 0 deletions
diff --git a/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.css b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.css
new file mode 100644
index 0000000000..a0095d8563
--- /dev/null
+++ b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.css
@@ -0,0 +1,56 @@
+/* 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/. */
+
+table.listing {
+ width: 100%;
+}
+
+table th, table td {
+ padding: 5px;
+ border: inset 2px black;
+ margin: 0;
+ width: 50%;
+ vertical-align: top;
+}
+
+hr {
+ clear: both;
+ margin: 10px;
+}
+
+iframe {
+ width: 100%;
+ height: 900px;
+}
+
+#player, #raw {
+ width: 800px;
+ margin-inline: auto;
+}
+
+#controls {
+ text-align: center;
+}
+
+#canvas {
+ border: solid 1px black;
+}
+
+#active {
+ width: 100%;
+ border: solid 1px black;
+ margin-top: 0;
+}
+
+#trace {
+ width: 100%;
+}
+
+#enabled {
+ color: red;
+}
+
+#enabled.enabled {
+ color: green;
+}
diff --git a/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.html b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.html
new file mode 100644
index 0000000000..8f15425a43
--- /dev/null
+++ b/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.html
@@ -0,0 +1,79 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta
+ http-equiv="Content-Security-Policy"
+ content="default-src chrome:; object-src 'none'"
+ />
+ <meta name="viewport" content="width=device-width" />
+ <meta charset="utf-8" />
+ <title>Checkerboard Analyzer</title>
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/aboutCheckerboard.css"
+ type="text/css"
+ />
+ </head>
+
+ <body>
+ <p>
+ Checkerboard recording is <span id="enabled">undetermined</span>.
+ <button id="enableToggleButton">Toggle it!</button>.
+ </p>
+ <p>
+ If there are active reports in progress, you can stop and flush them by
+ clicking here:
+ <button id="flushReportsButton">Flush active reports</button>
+ </p>
+ <table class="listing" cellspacing="0">
+ <tr>
+ <th>Most severe checkerboarding reports</th>
+ <th>Most recent checkerboarding reports</th>
+ </tr>
+ <tr>
+ <td><ul id="severe"></ul></td>
+ <td><ul id="recent"></ul></td>
+ </tr>
+ </table>
+
+ <hr />
+
+ <div id="player">
+ <div id="controls">
+ <button id="rewindButton">&#171;</button
+ ><!-- rewind button -->
+ <button id="stepBackButton">&lt;</button
+ ><!-- step back button -->
+ <button id="pauseButton">|| &#9654;</button
+ ><!-- pause button -->
+ <button id="stopButton">&#9744;</button
+ ><!-- stop button -->
+ <button id="stepForwardButton">&gt;</button
+ ><!-- step forward button -->
+ <button id="forwardButton">&#187;</button
+ ><!-- forward button -->
+ </div>
+ <canvas id="canvas" width="800" height="600"
+ >Canvas not supported!</canvas
+ >
+ <pre id="active">(Details for currently visible replay frame)</pre>
+ </div>
+
+ <hr />
+
+ <div id="raw">
+ Raw log:<br />
+ <textarea id="trace" rows="10"></textarea>
+ <div>
+ <input type="checkbox" id="excludePageFromZoom" /><label
+ for="excludePageFromZoom"
+ >Exclude page coordinates from zoom calculations</label
+ ><br />
+ </div>
+ </div>
+ </body>
+ <script src="chrome://global/content/aboutCheckerboard.js"></script>
+</html>
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);