blob: ddd9ebbd84e15ce0a736093a976df63ab2e0483c [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>Debug</title>
<link rel="stylesheet" href="../../UserInterface/External/CodeMirror/codemirror.css">
<link rel="stylesheet" href="codemirror-additions.css">
<script src="Utilities.js"></script>
<script src="../../UserInterface/External/CodeMirror/codemirror.js"></script>
<script src="../../UserInterface/External/CodeMirror/javascript.js"></script>
<script src="../../UserInterface/External/CodeMirror/css.js"></script>
<script src="../../UserInterface/Base/WebInspector.js"></script>
<script src="../../UserInterface/Views/CodeMirrorAdditions.js"></script>
<script src="../../UserInterface/Controllers/Formatter.js"></script>
<script src="FormatterDebug.js"></script>
<script src="../../UserInterface/Workers/Formatter/FormatterContentBuilder.js"></script>
<script src="../../UserInterface/Views/CodeMirrorFormatters.js"></script>
</head>
<body>
<h1>Debug Pretty Printing</h1>
<!-- Controls -->
<select id="mode">
<option selected value="text/javascript">JavaScript</option>
<option value="text/css">CSS</option>
<option value="css-rule">CSS-Rule</option>
</select>
<button id="populate">Populate</button>
<button id="run-tests">Run Tests</button>
<button id="clear">Clear</button>
<button id="select-output">Select Output</button>
<button id="run-again">Run Again</button>
<button id="save-as-url">Save URL</button>
<button id="save-local-storage">Save to Storage</button>
<button id="clear-local-storage">Clear Storage</button>
<small id="time"></small>
<br><br>
<!-- Editor -->
<textarea id="code" name="code"></textarea>
<!-- Output -->
<pre id="pretty"></pre>
<pre id="debug"></pre>
<script>
// Editor.
var cm = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
});
// In the frontend, keywords are added to the css mode to force the parser to recognize them as values.
// Here, the "calc" keyword is added to allow the css-rule tests to perform as they would in the frontend.
var modeSpec = CodeMirror.resolveMode("text/css");
modeSpec.valueKeywords["calc"] = true;
// Initial values from URL.
var queryParams = {};
if (window.location.search.length > 0) {
var searchString = window.location.search.substring(1);
var groups = searchString.split("&");
for (var i = 0; i < groups.length; ++i) {
var pair = groups[i].split("=");
queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
}
// Initial mode and string.
var mode = "text/javascript";
var content = "(function() { var a=1; return a+1; })();";
var updatePicker = false;
if (queryParams.content || queryParams.mode) {
content = queryParams.content || "";
mode = queryParams.mode || "text/javascript";
updatePicker = true;
} else if (localStorage.content || localStorage.mode) {
content = localStorage.content || "";
mode = localStorage.mode || "text/javascript";
updatePicker = true;
}
// Initial mode picker value.
var modePicker = document.getElementById("mode");
if (updatePicker) {
for (var i = 0; i < modePicker.options.length; ++i) {
if (modePicker.options[i].value === mode) {
modePicker.options[i].selected = true;
break;
}
}
}
// Set on CodeMirror.
cm.setValue(content);
cm.setOption("mode", mode);
// Changing the mode.
modePicker.addEventListener("change", function(event) {
cm.setValue("");
cm.setOption("mode", modePicker.value);
refresh();
});
// Populate button to populate with some canned content.
document.getElementById("populate").addEventListener("click", function(event) {
switch (modePicker.value) {
case "text/javascript":
var url = "../../../../LayoutTests/inspector/formatting/resources/javascript-tests/sample-jquery.js";
break;
case "text/css":
var url = "populate/apple.css";
break;
}
var xhr = new XMLHttpRequest;
xhr.open("GET", url, true);
xhr.onload = function() {
cm.setValue(xhr.responseText);
setTimeout(refresh);
}
xhr.send();
});
// Run Tests button.
document.getElementById("run-tests").addEventListener("click", function(event) {
cm.setValue("/* Running Tests... */");
refresh();
runTests();
});
// Clear button.
document.getElementById("clear").addEventListener("click", function(event) {
cm.setValue("");
refresh();
});
// Select output button.
document.getElementById("select-output").addEventListener("click", function(event) {
var range = document.createRange();
range.selectNodeContents(document.getElementById("pretty"));
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
});
// Run again button.
document.getElementById("run-again").addEventListener("click", function(event) {
refresh();
});
// Save as URL button.
document.getElementById("save-as-url").addEventListener("click", function(event) {
var mode = modePicker.value;
var content = cm.getValue();
window.location.search = "?mode=" + window.encodeURIComponent(mode) + "&content=" + window.encodeURIComponent(content);
});
// Save to localStorage.
document.getElementById("save-local-storage").addEventListener("click", function(event) {
localStorage.mode = modePicker.value;
localStorage.content = cm.getValue();
});
// Clear localStorage.
document.getElementById("clear-local-storage").addEventListener("click", function(event) {
localStorage.removeItem("mode");
localStorage.removeItem("content");
});
// Button helpers.
var buttons = ["mode", "populate", "run-tests", "clear", "select-output", "run-again"];
function disableButtons() {
buttons.forEach(function(id) {
document.getElementById(id).disabled = true;
});
}
function enableButtons() {
buttons.forEach(function(id) {
document.getElementById(id).disabled = false;
});
}
// Refresh after changes after a short delay.
var timer = null;
cm.on("change", function(codeMirror, change) {
if (timer)
clearTimeout(timer)
timer = setTimeout(function() {
clearTimeout(timer);
timer = null;
refresh();
}, 500);
});
// Output elements.
var timeOutput = document.getElementById("time");
var prettyPre = document.getElementById("pretty");
var debugPre = document.getElementById("debug");
function refresh() {
if (timer)
clearTimeout(timer);
const start = {line: 0, ch: 0};
const end = {line: cm.lineCount() - 1};
// Setup.
const indentString = " ";
var builder = new FormatterContentBuilder(indentString);
var formatter = new WI.Formatter(cm, builder);
// Time the formatter.
var startTime = Date.now();
formatter.format(start, end);
var endTime = Date.now();
// Gather debug information.
var debug = formatter.debug(start, end);
// Output the results.
timeOutput.innerText = (endTime - startTime) + "ms";
prettyPre.innerText = builder.formattedContent;
debugPre.innerText = debug;
}
setTimeout(refresh);
// Tests.
function runTests() {
disableButtons();
function completedCallback() {
enableButtons();
}
if (modePicker.value === "text/javascript")
runJavaScriptTests(completedCallback);
else if (modePicker.value === "css-rule")
runCssRuleTests(completedCallback);
else
runCSSTests(completedCallback);
}
function runJavaScriptTests(callback) {
_runTests(callback, [
"javascript-tests/block-comment.js",
"javascript-tests/single-statement-blocks.js",
"javascript-tests/switch-case-default.js",
]);
}
function runCssRuleTests(callback) {
_runTests(callback, [
"css-rule-tests/invalid-property-is-not-removed.css",
"css-rule-tests/remove-newline-between-values.css",
"css-rule-tests/remove-whitespace-before-colon.css",
"css-rule-tests/remove-whitespace-before-semicolon.css",
"css-rule-tests/remove-whitespace-before-property.css",
"css-rule-tests/remove-whitespace-before-prefixed-property.css",
"css-rule-tests/remove-whitespace-before-invalid-property.css",
"css-rule-tests/remove-whitespace-before-comment.css",
"css-rule-tests/split-comment-followed-by-property.css",
"css-rule-tests/split-comment-followed-by-prefixed-property.css",
"css-rule-tests/split-comment-followed-by-invalid-property.css",
"css-rule-tests/split-comment-followed-by-comment.css",
"css-rule-tests/split-property-followed-by-property.css",
"css-rule-tests/split-property-followed-by-prefixed-property.css",
"css-rule-tests/split-property-followed-by-invalid-property.css",
"css-rule-tests/split-property-followed-by-comment.css",
"css-rule-tests/split-invalid-property-followed-by-property.css",
"css-rule-tests/split-invalid-property-followed-by-prefixed-property.css",
"css-rule-tests/split-invalid-property-followed-by-invalid-property.css",
"css-rule-tests/split-invalid-property-followed-by-comment.css",
"css-rule-tests/split-property-without-semicolon-followed-by-comment-and-property.css",
"css-rule-tests/add-whitespace-between-values.css",
"css-rule-tests/add-whitespace-between-rules.css",
"css-rule-tests/add-whitespace-after-colon.css",
"css-rule-tests/add-whitespace-after-comma.css",
"css-rule-tests/do-not-append-semicolon.css",
"css-rule-tests/do-not-add-whitespace-before-prefixed-property-value.css",
"css-rule-tests/keep-prefixed-value.css",
]);
}
function runCSSTests(callback) {
_runTests(callback, [
"css-tests/basic.css",
"css-tests/calc.css",
"css-tests/gradient.css",
"css-tests/keyframes.css",
"css-tests/media-query.css",
"css-tests/selectors.css",
"css-tests/wrapping.css",
]);
}
function _runTests(callback, manifest) {
var index = -1;
var results = [];
setTimeout(runNextTest, 0);
function runNextTest() {
// Next test.
index++;
// Done.
if (index >= manifest.length) {
if (!index)
results.push("/* No tests for mode: " + modePicker.value);
printResults();
return;
}
// Load test and expected results.
var test = manifest[index];
var testURL = "../../../../LayoutTests/inspector/codemirror/resources/prettyprinting/" + test;
var expectedURL = testURL.replace(/\.([^\.]+)$/, "-expected.$1");
var xhr1 = new XMLHttpRequest;
xhr1.open("GET", testURL, false);
xhr1.send();
var xhr2 = new XMLHttpRequest;
xhr2.open("GET", expectedURL, false);
xhr2.send();
var testData = xhr1.responseText;
var expectedData = xhr2.responseText;
// Run the test.
var editor = CodeMirror(document.createElement("div"));
editor.setOption("mode", modePicker.value);
editor.setValue(testData);
const start = {line: 0, ch: 0};
const end = {line: editor.lineCount() - 1};
const indentString = " ";
var builder = new FormatterContentBuilder(indentString);
var formatter = new WI.Formatter(editor, builder);
formatter.format(start, end);
// Compare results.
var pass = builder.formattedContent === expectedData;
results.push("/* " + (pass ? "PASS" : "FAIL") + ": " + test + " */");
// Output failures to console.
if (!pass) {
console.log("Test", test);
console.log("Formatted Output", builder.formattedContent.length);
console.log(builder.formattedContent);
console.log("Expected Output", expectedData.length);
console.log(expectedData);
}
runNextTest();
}
function printResults() {
cm.setValue(results.join("\n"));
cm.refresh();
callback();
}
}
</script>
</body>
</html>