| <!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> |