123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- /* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
- "use strict";
- var Cu = Components.utils;
- const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
- const {RuleRewriter} = require("devtools/shared/css/parsing-utils");
- const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
- const TEST_DATA = [
- {
- desc: "simple set",
- input: "p:v;",
- instruction: {type: "set", name: "p", value: "N", priority: "",
- index: 0},
- expected: "p:N;"
- },
- {
- desc: "simple set clearing !important",
- input: "p:v !important;",
- instruction: {type: "set", name: "p", value: "N", priority: "",
- index: 0},
- expected: "p:N;"
- },
- {
- desc: "simple set adding !important",
- input: "p:v;",
- instruction: {type: "set", name: "p", value: "N", priority: "important",
- index: 0},
- expected: "p:N !important;"
- },
- {
- desc: "simple set between comments",
- input: "/*color:red;*/ p:v; /*color:green;*/",
- instruction: {type: "set", name: "p", value: "N", priority: "",
- index: 1},
- expected: "/*color:red;*/ p:N; /*color:green;*/"
- },
- // The rule view can generate a "set" with a previously unknown
- // property index; which should work like "create".
- {
- desc: "set at unknown index",
- input: "a:b; e: f;",
- instruction: {type: "set", name: "c", value: "d", priority: "",
- index: 2},
- expected: "a:b; e: f;c: d;"
- },
- {
- desc: "simple rename",
- input: "p:v;",
- instruction: {type: "rename", name: "p", newName: "q", index: 0},
- expected: "q:v;"
- },
- // "rename" is passed the name that the user entered, and must do
- // any escaping necessary to ensure that this is an identifier.
- {
- desc: "rename requiring escape",
- input: "p:v;",
- instruction: {type: "rename", name: "p", newName: "a b", index: 0},
- expected: "a\\ b:v;"
- },
- {
- desc: "simple create",
- input: "",
- instruction: {type: "create", name: "p", value: "v", priority: "important",
- index: 0, enabled: true},
- expected: "p: v !important;"
- },
- {
- desc: "create between two properties",
- input: "a:b; e: f;",
- instruction: {type: "create", name: "c", value: "d", priority: "",
- index: 1, enabled: true},
- expected: "a:b; c: d;e: f;"
- },
- // "create" is passed the name that the user entered, and must do
- // any escaping necessary to ensure that this is an identifier.
- {
- desc: "create requiring escape",
- input: "",
- instruction: {type: "create", name: "a b", value: "d", priority: "",
- index: 1, enabled: true},
- expected: "a\\ b: d;"
- },
- {
- desc: "simple disable",
- input: "p:v;",
- instruction: {type: "enable", name: "p", value: false, index: 0},
- expected: "/*! p:v; */"
- },
- {
- desc: "simple enable",
- input: "/* color:v; */",
- instruction: {type: "enable", name: "color", value: true, index: 0},
- expected: "color:v;"
- },
- {
- desc: "enable with following property in comment",
- input: "/* color:red; color: blue; */",
- instruction: {type: "enable", name: "color", value: true, index: 0},
- expected: "color:red; /* color: blue; */"
- },
- {
- desc: "enable with preceding property in comment",
- input: "/* color:red; color: blue; */",
- instruction: {type: "enable", name: "color", value: true, index: 1},
- expected: "/* color:red; */ color: blue;"
- },
- {
- desc: "simple remove",
- input: "a:b;c:d;e:f;",
- instruction: {type: "remove", name: "c", index: 1},
- expected: "a:b;e:f;"
- },
- {
- desc: "disable with comment ender in string",
- input: "content: '*/';",
- instruction: {type: "enable", name: "content", value: false, index: 0},
- expected: "/*! content: '*\\/'; */"
- },
- {
- desc: "enable with comment ender in string",
- input: "/* content: '*\\/'; */",
- instruction: {type: "enable", name: "content", value: true, index: 0},
- expected: "content: '*/';"
- },
- {
- desc: "enable requiring semicolon insertion",
- // Note the lack of a trailing semicolon in the comment.
- input: "/* color:red */ color: blue;",
- instruction: {type: "enable", name: "color", value: true, index: 0},
- expected: "color:red; color: blue;"
- },
- {
- desc: "create requiring semicolon insertion",
- // Note the lack of a trailing semicolon.
- input: "color: red",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "color: red;a: b;"
- },
- // Newline insertion.
- {
- desc: "simple newline insertion",
- input: "\ncolor: red;\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\ncolor: red;\na: b;\n"
- },
- // Newline insertion.
- {
- desc: "semicolon insertion before newline",
- // Note the lack of a trailing semicolon.
- input: "\ncolor: red\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\ncolor: red;\na: b;\n"
- },
- // Newline insertion.
- {
- desc: "newline and semicolon insertion",
- // Note the lack of a trailing semicolon and newline.
- input: "\ncolor: red",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\ncolor: red;\na: b;\n"
- },
- // Newline insertion and indentation.
- {
- desc: "indentation with create",
- input: "\n color: red;\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\n color: red;\n a: b;\n"
- },
- // Newline insertion and indentation.
- {
- desc: "indentation plus semicolon insertion before newline",
- // Note the lack of a trailing semicolon.
- input: "\n color: red\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\n color: red;\n a: b;\n"
- },
- {
- desc: "indentation inserted before trailing whitespace",
- // Note the trailing whitespace. This could come from a rule
- // like:
- // @supports (mumble) {
- // body {
- // color: red;
- // }
- // }
- // Here if we create a rule we don't want it to follow
- // the indentation of the "}".
- input: "\n color: red;\n ",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\n color: red;\n a: b;\n "
- },
- // Newline insertion and indentation.
- {
- desc: "indentation comes from preceding comment",
- // Note how the comment comes before the declaration.
- input: "\n /* comment */ color: red\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 1, enabled: true},
- expected: "\n /* comment */ color: red;\n a: b;\n"
- },
- // Default indentation.
- {
- desc: "use of default indentation",
- input: "\n",
- instruction: {type: "create", name: "a", value: "b", priority: "",
- index: 0, enabled: true},
- expected: "\n\ta: b;\n"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion removes newline",
- input: "a:b;\nc:d;\ne:f;",
- instruction: {type: "remove", name: "c", index: 1},
- expected: "a:b;\ne:f;"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion remove blank line",
- input: "\n a:b;\n c:d; \ne:f;",
- instruction: {type: "remove", name: "c", index: 1},
- expected: "\n a:b;\ne:f;"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion leaves comment",
- input: "\n a:b;\n /* something */ c:d; \ne:f;",
- instruction: {type: "remove", name: "c", index: 1},
- expected: "\n a:b;\n /* something */ \ne:f;"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion leaves previous newline",
- input: "\n a:b;\n c:d; \ne:f;",
- instruction: {type: "remove", name: "e", index: 2},
- expected: "\n a:b;\n c:d; \n"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion removes trailing whitespace",
- input: "\n a:b;\n c:d; \n e:f;",
- instruction: {type: "remove", name: "e", index: 2},
- expected: "\n a:b;\n c:d; \n"
- },
- // Deletion handles newlines properly.
- {
- desc: "deletion preserves indentation",
- input: " a:b;\n c:d; \n e:f;",
- instruction: {type: "remove", name: "a", index: 0},
- expected: " c:d; \n e:f;"
- },
- // Termination insertion corner case.
- {
- desc: "enable single quote termination",
- input: "/* content: 'hi */ color: red;",
- instruction: {type: "enable", name: "content", value: true, index: 0},
- expected: "content: 'hi'; color: red;",
- changed: {0: "'hi'"}
- },
- // Termination insertion corner case.
- {
- desc: "create single quote termination",
- input: "content: 'hi",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "content: 'hi';color: red;",
- changed: {0: "'hi'"}
- },
- // Termination insertion corner case.
- {
- desc: "enable double quote termination",
- input: "/* content: \"hi */ color: red;",
- instruction: {type: "enable", name: "content", value: true, index: 0},
- expected: "content: \"hi\"; color: red;",
- changed: {0: "\"hi\""}
- },
- // Termination insertion corner case.
- {
- desc: "create double quote termination",
- input: "content: \"hi",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "content: \"hi\";color: red;",
- changed: {0: "\"hi\""}
- },
- // Termination insertion corner case.
- {
- desc: "enable url termination",
- input: "/* background-image: url(something.jpg */ color: red;",
- instruction: {type: "enable", name: "background-image", value: true,
- index: 0},
- expected: "background-image: url(something.jpg); color: red;",
- changed: {0: "url(something.jpg)"}
- },
- // Termination insertion corner case.
- {
- desc: "create url termination",
- input: "background-image: url(something.jpg",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "background-image: url(something.jpg);color: red;",
- changed: {0: "url(something.jpg)"}
- },
- // Termination insertion corner case.
- {
- desc: "enable url single quote termination",
- input: "/* background-image: url('something.jpg */ color: red;",
- instruction: {type: "enable", name: "background-image", value: true,
- index: 0},
- expected: "background-image: url('something.jpg'); color: red;",
- changed: {0: "url('something.jpg')"}
- },
- // Termination insertion corner case.
- {
- desc: "create url single quote termination",
- input: "background-image: url('something.jpg",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "background-image: url('something.jpg');color: red;",
- changed: {0: "url('something.jpg')"}
- },
- // Termination insertion corner case.
- {
- desc: "create url double quote termination",
- input: "/* background-image: url(\"something.jpg */ color: red;",
- instruction: {type: "enable", name: "background-image", value: true,
- index: 0},
- expected: "background-image: url(\"something.jpg\"); color: red;",
- changed: {0: "url(\"something.jpg\")"}
- },
- // Termination insertion corner case.
- {
- desc: "enable url double quote termination",
- input: "background-image: url(\"something.jpg",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "background-image: url(\"something.jpg\");color: red;",
- changed: {0: "url(\"something.jpg\")"}
- },
- // Termination insertion corner case.
- {
- desc: "create backslash termination",
- input: "something: \\",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "something: \\\\;color: red;",
- // The lexer rewrites the token before we see it. However this is
- // so obscure as to be inconsequential.
- changed: {0: "\uFFFD\\"}
- },
- // Termination insertion corner case.
- {
- desc: "enable backslash single quote termination",
- input: "something: '\\",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "something: '\\\\';color: red;",
- changed: {0: "'\\\\'"}
- },
- {
- desc: "enable backslash double quote termination",
- input: "something: \"\\",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "something: \"\\\\\";color: red;",
- changed: {0: "\"\\\\\""}
- },
- // Termination insertion corner case.
- {
- desc: "enable comment termination",
- input: "something: blah /* comment ",
- instruction: {type: "create", name: "color", value: "red", priority: "",
- index: 1, enabled: true},
- expected: "something: blah /* comment*/; color: red;"
- },
- // Rewrite a "heuristic override" comment.
- {
- desc: "enable with heuristic override comment",
- input: "/*! walrus: zebra; */",
- instruction: {type: "enable", name: "walrus", value: true, index: 0},
- expected: "walrus: zebra;"
- },
- // Sanitize a bad value.
- {
- desc: "create sanitize unpaired brace",
- input: "",
- instruction: {type: "create", name: "p", value: "}", priority: "",
- index: 0, enabled: true},
- expected: "p: \\};",
- changed: {0: "\\}"}
- },
- // Sanitize a bad value.
- {
- desc: "set sanitize unpaired brace",
- input: "walrus: zebra;",
- instruction: {type: "set", name: "walrus", value: "{{}}}", priority: "",
- index: 0},
- expected: "walrus: {{}}\\};",
- changed: {0: "{{}}\\}"}
- },
- // Sanitize a bad value.
- {
- desc: "enable sanitize unpaired brace",
- input: "/*! walrus: }*/",
- instruction: {type: "enable", name: "walrus", value: true, index: 0},
- expected: "walrus: \\};",
- changed: {0: "\\}"}
- },
- // Creating a new declaration does not require an attempt to
- // terminate a previous commented declaration.
- {
- desc: "disabled declaration does not need semicolon insertion",
- input: "/*! no: semicolon */\n",
- instruction: {type: "create", name: "walrus", value: "zebra", priority: "",
- index: 1, enabled: true},
- expected: "/*! no: semicolon */\nwalrus: zebra;\n",
- changed: {}
- },
- {
- desc: "create commented-out property",
- input: "p: v",
- instruction: {type: "create", name: "shoveler", value: "duck", priority: "",
- index: 1, enabled: false},
- expected: "p: v;/*! shoveler: duck; */",
- },
- {
- desc: "disabled create with comment ender in string",
- input: "",
- instruction: {type: "create", name: "content", value: "'*/'", priority: "",
- index: 0, enabled: false},
- expected: "/*! content: '*\\/'; */"
- },
- {
- desc: "delete disabled property",
- input: "\n a:b;\n /* color:#f0c; */\n e:f;",
- instruction: {type: "remove", name: "color", index: 1},
- expected: "\n a:b;\n e:f;",
- },
- {
- desc: "delete heuristic-disabled property",
- input: "\n a:b;\n /*! c:d; */\n e:f;",
- instruction: {type: "remove", name: "c", index: 1},
- expected: "\n a:b;\n e:f;",
- },
- {
- desc: "delete disabled property leaving other disabled property",
- input: "\n a:b;\n /* color:#f0c; background-color: seagreen; */\n e:f;",
- instruction: {type: "remove", name: "color", index: 1},
- expected: "\n a:b;\n /* background-color: seagreen; */\n e:f;",
- },
- ];
- function rewriteDeclarations(inputString, instruction, defaultIndentation) {
- let rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
- rewriter.defaultIndentation = defaultIndentation;
- switch (instruction.type) {
- case "rename":
- rewriter.renameProperty(instruction.index, instruction.name,
- instruction.newName);
- break;
- case "enable":
- rewriter.setPropertyEnabled(instruction.index, instruction.name,
- instruction.value);
- break;
- case "create":
- rewriter.createProperty(instruction.index, instruction.name,
- instruction.value, instruction.priority,
- instruction.enabled);
- break;
- case "set":
- rewriter.setProperty(instruction.index, instruction.name,
- instruction.value, instruction.priority);
- break;
- case "remove":
- rewriter.removeProperty(instruction.index, instruction.name);
- break;
- default:
- throw new Error("unrecognized instruction");
- }
- return rewriter.getResult();
- }
- function run_test() {
- for (let test of TEST_DATA) {
- let {changed, text} = rewriteDeclarations(test.input, test.instruction,
- "\t");
- equal(text, test.expected, "output for " + test.desc);
- let expectChanged;
- if ("changed" in test) {
- expectChanged = test.changed;
- } else {
- expectChanged = {};
- }
- deepEqual(changed, expectChanged, "changed result for " + test.desc);
- }
- }
|