Bug 1238042 - Add about:checkerboard. r=ehsan,botond

This commit is contained in:
Kartikaya Gupta 2016-01-22 20:27:28 -05:00
parent ef957ce9f2
commit ec251db847
9 changed files with 403 additions and 7 deletions

View File

@ -44,6 +44,11 @@ static RedirEntry kRedirMap[] = {
"buildconfig", "chrome://global/content/buildconfig.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
},
{
"checkerboard", "chrome://global/content/aboutCheckerboard.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT
},
{ "config", "chrome://global/content/config.xul", 0 },
#ifdef MOZ_CRASHREPORTER
{ "crashes", "chrome://global/content/crashes.xhtml", 0 },

View File

@ -24,12 +24,9 @@ protected:
virtual ~nsAboutRedirector() {}
};
#define NS_ABOUT_REDIRECTOR_MODULE_CID \
{ /* f0acde16-1dd1-11b2-9e35-f5786fff5a66*/ \
0xf0acde16, \
0x1dd1, \
0x11b2, \
{0x9e, 0x35, 0xf5, 0x78, 0x6f, 0xff, 0x5a, 0x66} \
}
/* 56ebedd4-6ccf-48e8-bdae-adc77f044567 */
#define NS_ABOUT_REDIRECTOR_MODULE_CID \
{ 0x56ebedd4, 0x6ccf, 0x48e8, \
{ 0xbd, 0xae, 0xad, 0xc7, 0x7f, 0x04, 0x45, 0x67 } }
#endif // nsAboutRedirector_h__

View File

@ -163,6 +163,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "about", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "buildconfig", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "checkerboard", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "config", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
#ifdef MOZ_CRASHREPORTER
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "crashes", &kNS_ABOUT_REDIRECTOR_MODULE_CID },

View File

@ -0,0 +1,49 @@
/* 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: 0px;
width: 50%;
vertical-align: top;
}
hr {
clear: both;
margin: 10px;
}
iframe {
width: 100%;
height: 900px;
}
#player, #raw {
width: 800px;
margin-left: auto;
margin-right: auto;
}
#controls {
text-align: center;
}
#canvas {
border: solid 1px black;
}
#active {
width: 100%;
border: solid 1px black;
margin-top: 0;
}
#trace {
width: 100%;
}

View File

@ -0,0 +1,272 @@
/* 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 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();
}

View File

@ -0,0 +1,53 @@
<?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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width"/>
<link rel="stylesheet" href="chrome://global/content/aboutCheckerboard.css" type="text/css"/>
<script type="text/javascript;version=1.8" src="chrome://global/content/aboutCheckerboard.js"></script>
</head>
<body onload="onLoad()">
<p>Checkerboard recording is <span id="enabled" style="color: red">undetermined</span>.
<button onclick="toggleEnabled()">Toggle it!</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 onclick="reset(true)">&#171;</button><!-- rewind button -->
<button onclick="step(true)">&lt;</button><!-- step back button -->
<button onclick="togglePlay()">|| &#9654;</button><!-- pause button -->
<button onclick="stopPlay()">&#9744;</button><!-- stop button -->
<button onclick="step(false)">&gt;</button><!-- step forward button -->
<button onclick="reset(false)">&#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" onclick="loadData()"/><label for="excludePageFromZoom">Exclude page coordinates from zoom calculations</label><br/>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,8 @@
# 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/.
toolkit.jar:
content/global/aboutCheckerboard.js (content/aboutCheckerboard.js)
content/global/aboutCheckerboard.xhtml (content/aboutCheckerboard.xhtml)
content/global/aboutCheckerboard.css (content/aboutCheckerboard.css)

View File

@ -0,0 +1,10 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
JAR_MANIFESTS += ['jar.mn']
with Files('**'):
BUG_COMPONENT = ('Core', 'Panning and Zooming')

View File

@ -10,6 +10,7 @@ if CONFIG['MOZ_ENABLE_XREMOTE']:
DIRS += [
'aboutcache',
'aboutcheckerboard',
'aboutmemory',
'addoncompat',
'alerts',