tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_rewriteDeclarations.js (22219B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const RuleRewriter = require("resource://devtools/client/fronts/inspector/rule-rewriter.js");
      7 const {
      8  isCssPropertyKnown,
      9 } = require("resource://devtools/server/actors/css-properties.js");
     10 
     11 const TEST_DATA = [
     12  {
     13    desc: "simple set",
     14    input: "p:v;",
     15    instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
     16    expected: "p:N;",
     17  },
     18  {
     19    desc: "set in rule with nested rule",
     20    input: "a:b; span { e:f; }",
     21    instruction: { type: "set", name: "a", value: "N", priority: "", index: 0 },
     22    expected: "a:N; span { e:f; }",
     23  },
     24  {
     25    desc: "simple set clearing !important",
     26    input: "p:v !important;",
     27    instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
     28    expected: "p:N;",
     29  },
     30  {
     31    desc: "simple set adding !important",
     32    input: "p:v;",
     33    instruction: {
     34      type: "set",
     35      name: "p",
     36      value: "N",
     37      priority: "important",
     38      index: 0,
     39    },
     40    expected: "p:N !important;",
     41  },
     42  {
     43    desc: "simple set between comments",
     44    input: "/*color:red;*/ p:v; /*color:green;*/",
     45    instruction: { type: "set", name: "p", value: "N", priority: "", index: 1 },
     46    expected: "/*color:red;*/ p:N; /*color:green;*/",
     47  },
     48  // The rule view can generate a "set" with a previously unknown
     49  // property index; which should work like "create".
     50  {
     51    desc: "set at unknown index",
     52    input: "a:b; e: f;",
     53    instruction: { type: "set", name: "c", value: "d", priority: "", index: 2 },
     54    expected: "a:b; e: f;c: d;",
     55  },
     56  {
     57    desc: "simple rename",
     58    input: "p:v;",
     59    instruction: { type: "rename", name: "p", newName: "q", index: 0 },
     60    expected: "q:v;",
     61  },
     62  {
     63    desc: "rename in rule with nested rule",
     64    input: "a:b; span { e:f; }",
     65    instruction: { type: "rename", name: "a", newName: "q", index: 0 },
     66    expected: "q:b; span { e:f; }",
     67  },
     68  // "rename" is passed the name that the user entered, and must do
     69  // any escaping necessary to ensure that this is an identifier.
     70  {
     71    desc: "rename requiring escape",
     72    input: "p:v;",
     73    instruction: { type: "rename", name: "p", newName: "a b", index: 0 },
     74    expected: "a\\ b:v;",
     75  },
     76  {
     77    desc: "simple create",
     78    input: "",
     79    instruction: {
     80      type: "create",
     81      name: "p",
     82      value: "v",
     83      priority: "important",
     84      index: 0,
     85      enabled: true,
     86    },
     87    expected: "p: v !important;",
     88  },
     89  {
     90    desc: "create between two properties",
     91    input: "a:b; e: f;",
     92    instruction: {
     93      type: "create",
     94      name: "c",
     95      value: "d",
     96      priority: "",
     97      index: 1,
     98      enabled: true,
     99    },
    100    expected: "a:b; c: d;e: f;",
    101  },
    102  {
    103    desc: "create at the end of rule with nested rule",
    104    input: "a:b; span { e:f; }",
    105    instruction: {
    106      type: "create",
    107      name: "c",
    108      value: "d",
    109      priority: "",
    110      index: 1,
    111      enabled: true,
    112    },
    113    expected: "a:b; c: d;span { e:f; }",
    114  },
    115  {
    116    desc: "create at the end of rule with nested rule and line breaks",
    117    input: `
    118      position: absolute;
    119      &.class {}`,
    120    instruction: {
    121      type: "create",
    122      name: "color",
    123      value: "red",
    124      priority: "",
    125      index: 1,
    126      enabled: true,
    127    },
    128    expected: `
    129      position: absolute;
    130      color: red;
    131      &.class {}`,
    132  },
    133  {
    134    desc: "create at the end of rule with CSSNestedDeclarations",
    135    input: "a:b; span { e:f; } g: h;",
    136    instruction: {
    137      type: "create",
    138      name: "c",
    139      value: "d",
    140      priority: "",
    141      index: 1,
    142      enabled: true,
    143    },
    144    expected: "a:b; c: d;span { e:f; } g: h;",
    145  },
    146  // "create" is passed the name that the user entered, and must do
    147  // any escaping necessary to ensure that this is an identifier.
    148  {
    149    desc: "create requiring escape",
    150    input: "",
    151    instruction: {
    152      type: "create",
    153      name: "a b",
    154      value: "d",
    155      priority: "",
    156      index: 1,
    157      enabled: true,
    158    },
    159    expected: "a\\ b: d;",
    160  },
    161  {
    162    desc: "simple disable",
    163    input: "p:v;",
    164    instruction: { type: "enable", name: "p", value: false, index: 0 },
    165    expected: "/*! p:v; */",
    166  },
    167  {
    168    desc: "simple enable",
    169    input: "/* color:v; */",
    170    instruction: { type: "enable", name: "color", value: true, index: 0 },
    171    expected: "color:v;",
    172  },
    173  {
    174    desc: "enable with following property in comment",
    175    input: "/* color:red; color: blue; */",
    176    instruction: { type: "enable", name: "color", value: true, index: 0 },
    177    expected: "color:red; /* color: blue; */",
    178  },
    179  {
    180    desc: "enable with preceding property in comment",
    181    input: "/* color:red; color: blue; */",
    182    instruction: { type: "enable", name: "color", value: true, index: 1 },
    183    expected: "/* color:red; */ color: blue;",
    184  },
    185  {
    186    desc: "disable on rule with nested rule",
    187    input: "a:b; span { c:d; }",
    188    instruction: { type: "enable", name: "a", value: false, index: 0 },
    189    expected: "/*! a:b; */ span { c:d; }",
    190  },
    191  {
    192    desc: "enable on rule with nested rule",
    193    input: "/*! a:b; */ span { c:d; }",
    194    instruction: { type: "enable", name: "a", value: true, index: 0 },
    195    expected: "a:b; span { c:d; }",
    196  },
    197  {
    198    desc: "simple remove",
    199    input: "a:b;c:d;e:f;",
    200    instruction: { type: "remove", name: "c", index: 1 },
    201    expected: "a:b;e:f;",
    202  },
    203  {
    204    desc: "remove on rule with nested rule",
    205    input: "a:b;c:d;span { e:f; }",
    206    instruction: { type: "remove", name: "c", index: 1 },
    207    expected: "a:b;span { e:f; }",
    208  },
    209  {
    210    desc: "disable with comment ender in string",
    211    input: "content: '*/';",
    212    instruction: { type: "enable", name: "content", value: false, index: 0 },
    213    expected: "/*! content: '*\\/'; */",
    214  },
    215  {
    216    desc: "enable with comment ender in string",
    217    input: "/* content: '*\\/'; */",
    218    instruction: { type: "enable", name: "content", value: true, index: 0 },
    219    expected: "content: '*/';",
    220  },
    221  {
    222    desc: "enable requiring semicolon insertion",
    223    // Note the lack of a trailing semicolon in the comment.
    224    input: "/* color:red */ color: blue;",
    225    instruction: { type: "enable", name: "color", value: true, index: 0 },
    226    expected: "color:red; color: blue;",
    227  },
    228  {
    229    desc: "create requiring semicolon insertion",
    230    // Note the lack of a trailing semicolon.
    231    input: "color: red",
    232    instruction: {
    233      type: "create",
    234      name: "a",
    235      value: "b",
    236      priority: "",
    237      index: 1,
    238      enabled: true,
    239    },
    240    expected: "color: red;a: b;",
    241  },
    242 
    243  // Newline insertion.
    244  {
    245    desc: "simple newline insertion",
    246    input: "\ncolor: red;\n",
    247    instruction: {
    248      type: "create",
    249      name: "a",
    250      value: "b",
    251      priority: "",
    252      index: 1,
    253      enabled: true,
    254    },
    255    expected: "\ncolor: red;\na: b;\n",
    256  },
    257  // Newline insertion.
    258  {
    259    desc: "semicolon insertion before newline",
    260    // Note the lack of a trailing semicolon.
    261    input: "\ncolor: red\n",
    262    instruction: {
    263      type: "create",
    264      name: "a",
    265      value: "b",
    266      priority: "",
    267      index: 1,
    268      enabled: true,
    269    },
    270    expected: "\ncolor: red;\na: b;\n",
    271  },
    272  // Newline insertion.
    273  {
    274    desc: "newline and semicolon insertion",
    275    // Note the lack of a trailing semicolon and newline.
    276    input: "\ncolor: red",
    277    instruction: {
    278      type: "create",
    279      name: "a",
    280      value: "b",
    281      priority: "",
    282      index: 1,
    283      enabled: true,
    284    },
    285    expected: "\ncolor: red;\na: b;\n",
    286  },
    287 
    288  // Newline insertion and indentation.
    289  {
    290    desc: "indentation with create",
    291    input: "\n  color: red;\n",
    292    instruction: {
    293      type: "create",
    294      name: "a",
    295      value: "b",
    296      priority: "",
    297      index: 1,
    298      enabled: true,
    299    },
    300    expected: "\n  color: red;\n  a: b;\n",
    301  },
    302  // Newline insertion and indentation.
    303  {
    304    desc: "indentation plus semicolon insertion before newline",
    305    // Note the lack of a trailing semicolon.
    306    input: "\n  color: red\n",
    307    instruction: {
    308      type: "create",
    309      name: "a",
    310      value: "b",
    311      priority: "",
    312      index: 1,
    313      enabled: true,
    314    },
    315    expected: "\n  color: red;\n  a: b;\n",
    316  },
    317  {
    318    desc: "indentation inserted before trailing whitespace",
    319    // Note the trailing whitespace.  This could come from a rule
    320    // like:
    321    // @supports (mumble) {
    322    //   body {
    323    //     color: red;
    324    //   }
    325    // }
    326    // Here if we create a rule we don't want it to follow
    327    // the indentation of the "}".
    328    input: "\n    color: red;\n  ",
    329    instruction: {
    330      type: "create",
    331      name: "a",
    332      value: "b",
    333      priority: "",
    334      index: 1,
    335      enabled: true,
    336    },
    337    expected: "\n    color: red;\n    a: b;\n  ",
    338  },
    339  // Newline insertion and indentation.
    340  {
    341    desc: "indentation comes from preceding comment",
    342    // Note how the comment comes before the declaration.
    343    input: "\n  /* comment */ color: red\n",
    344    instruction: {
    345      type: "create",
    346      name: "a",
    347      value: "b",
    348      priority: "",
    349      index: 1,
    350      enabled: true,
    351    },
    352    expected: "\n  /* comment */ color: red;\n  a: b;\n",
    353  },
    354  // Default indentation.
    355  {
    356    desc: "use of default indentation",
    357    input: "\n",
    358    instruction: {
    359      type: "create",
    360      name: "a",
    361      value: "b",
    362      priority: "",
    363      index: 0,
    364      enabled: true,
    365    },
    366    expected: "\n\ta: b;\n",
    367  },
    368 
    369  // Deletion handles newlines properly.
    370  {
    371    desc: "deletion removes newline",
    372    input: "a:b;\nc:d;\ne:f;",
    373    instruction: { type: "remove", name: "c", index: 1 },
    374    expected: "a:b;\ne:f;",
    375  },
    376  // Deletion handles newlines properly.
    377  {
    378    desc: "deletion remove blank line",
    379    input: "\n  a:b;\n  c:d;  \ne:f;",
    380    instruction: { type: "remove", name: "c", index: 1 },
    381    expected: "\n  a:b;\ne:f;",
    382  },
    383  // Deletion handles newlines properly.
    384  {
    385    desc: "deletion leaves comment",
    386    input: "\n  a:b;\n  /* something */ c:d;  \ne:f;",
    387    instruction: { type: "remove", name: "c", index: 1 },
    388    expected: "\n  a:b;\n  /* something */   \ne:f;",
    389  },
    390  // Deletion handles newlines properly.
    391  {
    392    desc: "deletion leaves previous newline",
    393    input: "\n  a:b;\n  c:d;  \ne:f;",
    394    instruction: { type: "remove", name: "e", index: 2 },
    395    expected: "\n  a:b;\n  c:d;  \n",
    396  },
    397  // Deletion handles newlines properly.
    398  {
    399    desc: "deletion removes trailing whitespace",
    400    input: "\n  a:b;\n  c:d;  \n    e:f;",
    401    instruction: { type: "remove", name: "e", index: 2 },
    402    expected: "\n  a:b;\n  c:d;  \n",
    403  },
    404  // Deletion handles newlines properly.
    405  {
    406    desc: "deletion preserves indentation",
    407    input: "  a:b;\n  c:d;  \n    e:f;",
    408    instruction: { type: "remove", name: "a", index: 0 },
    409    expected: "  c:d;  \n    e:f;",
    410  },
    411 
    412  // Termination insertion corner case.
    413  {
    414    desc: "enable single quote termination",
    415    input: "/* content: 'hi */ color: red;",
    416    instruction: { type: "enable", name: "content", value: true, index: 0 },
    417    expected: "content: 'hi'; color: red;",
    418    changed: { 0: "'hi'" },
    419  },
    420  // Termination insertion corner case.
    421  {
    422    desc: "create single quote termination",
    423    input: "content: 'hi",
    424    instruction: {
    425      type: "create",
    426      name: "color",
    427      value: "red",
    428      priority: "",
    429      index: 1,
    430      enabled: true,
    431    },
    432    expected: "content: 'hi';color: red;",
    433    changed: { 0: "'hi'" },
    434  },
    435 
    436  // Termination insertion corner case.
    437  {
    438    desc: "enable double quote termination",
    439    input: '/* content: "hi */ color: red;',
    440    instruction: { type: "enable", name: "content", value: true, index: 0 },
    441    expected: 'content: "hi"; color: red;',
    442    changed: { 0: '"hi"' },
    443  },
    444  // Termination insertion corner case.
    445  {
    446    desc: "create double quote termination",
    447    input: 'content: "hi',
    448    instruction: {
    449      type: "create",
    450      name: "color",
    451      value: "red",
    452      priority: "",
    453      index: 1,
    454      enabled: true,
    455    },
    456    expected: 'content: "hi";color: red;',
    457    changed: { 0: '"hi"' },
    458  },
    459 
    460  // Termination insertion corner case.
    461  {
    462    desc: "enable url termination",
    463    input: "/* background-image: url(something.jpg */ color: red;",
    464    instruction: {
    465      type: "enable",
    466      name: "background-image",
    467      value: true,
    468      index: 0,
    469    },
    470    expected: "background-image: url(something.jpg); color: red;",
    471    changed: { 0: "url(something.jpg)" },
    472  },
    473  // Termination insertion corner case.
    474  {
    475    desc: "create url termination",
    476    input: "background-image: url(something.jpg",
    477    instruction: {
    478      type: "create",
    479      name: "color",
    480      value: "red",
    481      priority: "",
    482      index: 1,
    483      enabled: true,
    484    },
    485    expected: "background-image: url(something.jpg);color: red;",
    486    changed: { 0: "url(something.jpg)" },
    487  },
    488 
    489  // Termination insertion corner case.
    490  {
    491    desc: "enable url single quote termination",
    492    input: "/* background-image: url('something.jpg */ color: red;",
    493    instruction: {
    494      type: "enable",
    495      name: "background-image",
    496      value: true,
    497      index: 0,
    498    },
    499    expected: "background-image: url('something.jpg'); color: red;",
    500    changed: { 0: "url('something.jpg')" },
    501  },
    502  // Termination insertion corner case.
    503  {
    504    desc: "create url single quote termination",
    505    input: "background-image: url('something.jpg",
    506    instruction: {
    507      type: "create",
    508      name: "color",
    509      value: "red",
    510      priority: "",
    511      index: 1,
    512      enabled: true,
    513    },
    514    expected: "background-image: url('something.jpg');color: red;",
    515    changed: { 0: "url('something.jpg')" },
    516  },
    517 
    518  // Termination insertion corner case.
    519  {
    520    desc: "create url double quote termination",
    521    input: '/* background-image: url("something.jpg */ color: red;',
    522    instruction: {
    523      type: "enable",
    524      name: "background-image",
    525      value: true,
    526      index: 0,
    527    },
    528    expected: 'background-image: url("something.jpg"); color: red;',
    529    changed: { 0: 'url("something.jpg")' },
    530  },
    531  // Termination insertion corner case.
    532  {
    533    desc: "enable url double quote termination",
    534    input: 'background-image: url("something.jpg',
    535    instruction: {
    536      type: "create",
    537      name: "color",
    538      value: "red",
    539      priority: "",
    540      index: 1,
    541      enabled: true,
    542    },
    543    expected: 'background-image: url("something.jpg");color: red;',
    544    changed: { 0: 'url("something.jpg")' },
    545  },
    546 
    547  // Termination insertion corner case.
    548  {
    549    desc: "create backslash termination",
    550    input: "something: \\",
    551    instruction: {
    552      type: "create",
    553      name: "color",
    554      value: "red",
    555      priority: "",
    556      index: 1,
    557      enabled: true,
    558    },
    559    expected: "something: \\\\;color: red;",
    560    changed: { 0: "\\\\" },
    561  },
    562 
    563  // Termination insertion corner case.
    564  {
    565    desc: "enable backslash single quote termination",
    566    input: "something: '\\",
    567    instruction: {
    568      type: "create",
    569      name: "color",
    570      value: "red",
    571      priority: "",
    572      index: 1,
    573      enabled: true,
    574    },
    575    expected: "something: '\\\\';color: red;",
    576    changed: { 0: "'\\\\'" },
    577  },
    578  {
    579    desc: "enable backslash double quote termination",
    580    input: 'something: "\\',
    581    instruction: {
    582      type: "create",
    583      name: "color",
    584      value: "red",
    585      priority: "",
    586      index: 1,
    587      enabled: true,
    588    },
    589    expected: 'something: "\\\\";color: red;',
    590    changed: { 0: '"\\\\"' },
    591  },
    592 
    593  // Termination insertion corner case.
    594  {
    595    desc: "enable comment termination",
    596    input: "something: blah /* comment ",
    597    instruction: {
    598      type: "create",
    599      name: "color",
    600      value: "red",
    601      priority: "",
    602      index: 1,
    603      enabled: true,
    604    },
    605    expected: "something: blah /* comment*/; color: red;",
    606  },
    607 
    608  // Rewrite a "heuristic override" comment.
    609  {
    610    desc: "enable with heuristic override comment",
    611    input: "/*! walrus: zebra; */",
    612    instruction: { type: "enable", name: "walrus", value: true, index: 0 },
    613    expected: "walrus: zebra;",
    614  },
    615 
    616  // Sanitize a bad value.
    617  {
    618    desc: "create sanitize unpaired brace",
    619    input: "",
    620    instruction: {
    621      type: "create",
    622      name: "p",
    623      value: "}",
    624      priority: "",
    625      index: 0,
    626      enabled: true,
    627    },
    628    expected: "p: \\};",
    629    changed: { 0: "\\}" },
    630  },
    631  // Sanitize a bad value.
    632  {
    633    desc: "set sanitize unpaired brace",
    634    input: "walrus: zebra;",
    635    instruction: {
    636      type: "set",
    637      name: "walrus",
    638      value: "{{}}}",
    639      priority: "",
    640      index: 0,
    641    },
    642    expected: "walrus: {{}}\\};",
    643    changed: { 0: "{{}}\\}" },
    644  },
    645  // Sanitize a bad value.
    646  {
    647    desc: "enable sanitize unpaired brace",
    648    input: "/*! walrus: }*/",
    649    instruction: { type: "enable", name: "walrus", value: true, index: 0 },
    650    expected: "walrus: \\};",
    651    changed: { 0: "\\}" },
    652  },
    653 
    654  // Creating a new declaration does not require an attempt to
    655  // terminate a previous commented declaration.
    656  {
    657    desc: "disabled declaration does not need semicolon insertion",
    658    input: "/*! no: semicolon */\n",
    659    instruction: {
    660      type: "create",
    661      name: "walrus",
    662      value: "zebra",
    663      priority: "",
    664      index: 1,
    665      enabled: true,
    666    },
    667    expected: "/*! no: semicolon */\nwalrus: zebra;\n",
    668    changed: {},
    669  },
    670 
    671  {
    672    desc: "create commented-out property",
    673    input: "p: v",
    674    instruction: {
    675      type: "create",
    676      name: "shoveler",
    677      value: "duck",
    678      priority: "",
    679      index: 1,
    680      enabled: false,
    681    },
    682    expected: "p: v;/*! shoveler: duck; */",
    683  },
    684  {
    685    desc: "disabled create with comment ender in string",
    686    input: "",
    687    instruction: {
    688      type: "create",
    689      name: "content",
    690      value: "'*/'",
    691      priority: "",
    692      index: 0,
    693      enabled: false,
    694    },
    695    expected: "/*! content: '*\\/'; */",
    696  },
    697 
    698  {
    699    desc: "delete disabled property",
    700    input: "\n  a:b;\n  /* color:#f06; */\n  e:f;",
    701    instruction: { type: "remove", name: "color", index: 1 },
    702    expected: "\n  a:b;\n  e:f;",
    703  },
    704  {
    705    desc: "delete heuristic-disabled property",
    706    input: "\n  a:b;\n  /*! c:d; */\n  e:f;",
    707    instruction: { type: "remove", name: "c", index: 1 },
    708    expected: "\n  a:b;\n  e:f;",
    709  },
    710  {
    711    desc: "delete disabled property leaving other disabled property",
    712    input: "\n  a:b;\n  /* color:#f06; background-color: seagreen; */\n  e:f;",
    713    instruction: { type: "remove", name: "color", index: 1 },
    714    expected: "\n  a:b;\n   /* background-color: seagreen; */\n  e:f;",
    715  },
    716 
    717  {
    718    desc: "regression test for bug 1328016",
    719    input: "position:absolute;top50px;height:50px;width:50px;",
    720    instruction: {
    721      type: "set",
    722      name: "width",
    723      value: "60px",
    724      priority: "",
    725      index: 2,
    726    },
    727    expected: "position:absolute;top50px;height:50px;width:60px;",
    728  },
    729 
    730  {
    731    desc: "url regression test for bug 1321970",
    732    input: "",
    733    instruction: {
    734      type: "create",
    735      name: "p",
    736      value: "url(",
    737      priority: "",
    738      index: 0,
    739      enabled: true,
    740    },
    741    expected: "p: url();",
    742    changed: { 0: "url()" },
    743  },
    744 
    745  {
    746    desc: "url semicolon regression test for bug 1321970",
    747    input: "",
    748    instruction: {
    749      type: "create",
    750      name: "p",
    751      value: "url(;",
    752      priority: "",
    753      index: 0,
    754      enabled: true,
    755    },
    756    expected: "p: url();",
    757    changed: { 0: "url()" },
    758  },
    759 
    760  {
    761    desc: "basic regression test for bug 1321970",
    762    input: "",
    763    instruction: {
    764      type: "create",
    765      name: "p",
    766      value: "(",
    767      priority: "",
    768      index: 0,
    769      enabled: true,
    770    },
    771    expected: "p: \\(;",
    772    changed: { 0: "\\(" },
    773  },
    774 
    775  {
    776    desc: "unbalanced regression test for bug 1321970",
    777    input: "",
    778    instruction: {
    779      type: "create",
    780      name: "p",
    781      value: "({[})",
    782      priority: "",
    783      index: 0,
    784      enabled: true,
    785    },
    786    expected: "p: ({\\[});",
    787    changed: { 0: "({\\[})" },
    788  },
    789 
    790  {
    791    desc: "function regression test for bug 1321970",
    792    input: "",
    793    instruction: {
    794      type: "create",
    795      name: "p",
    796      value: "func(1,2)",
    797      priority: "",
    798      index: 0,
    799      enabled: true,
    800    },
    801    expected: "p: func(1,2);",
    802  },
    803 
    804  {
    805    desc: "function regression test for bug 1355233",
    806    input: "",
    807    instruction: {
    808      type: "create",
    809      name: "p",
    810      value: "func(",
    811      priority: "",
    812      index: 0,
    813      enabled: true,
    814    },
    815    expected: "p: func\\(;",
    816    changed: { 0: "func\\(" },
    817  },
    818 ];
    819 
    820 function rewriteDeclarations(
    821  win,
    822  inputString,
    823  instruction,
    824  defaultIndentation
    825 ) {
    826  const rewriter = new RuleRewriter(win, isCssPropertyKnown, null, inputString);
    827  rewriter.defaultIndentation = defaultIndentation;
    828 
    829  switch (instruction.type) {
    830    case "rename":
    831      rewriter.renameProperty(
    832        instruction.index,
    833        instruction.name,
    834        instruction.newName
    835      );
    836      break;
    837 
    838    case "enable":
    839      rewriter.setPropertyEnabled(
    840        instruction.index,
    841        instruction.name,
    842        instruction.value
    843      );
    844      break;
    845 
    846    case "create":
    847      rewriter.createProperty(
    848        instruction.index,
    849        instruction.name,
    850        instruction.value,
    851        instruction.priority,
    852        instruction.enabled
    853      );
    854      break;
    855 
    856    case "set":
    857      rewriter.setProperty(
    858        instruction.index,
    859        instruction.name,
    860        instruction.value,
    861        instruction.priority
    862      );
    863      break;
    864 
    865    case "remove":
    866      rewriter.removeProperty(instruction.index, instruction.name);
    867      break;
    868 
    869    default:
    870      throw new Error("unrecognized instruction");
    871  }
    872 
    873  return rewriter.getResult();
    874 }
    875 
    876 function run_test() {
    877  const win =
    878    Services.appShell.createWindowlessBrowser(false).document.defaultView;
    879 
    880  for (const test of TEST_DATA) {
    881    const { changed, text } = rewriteDeclarations(
    882      win,
    883      test.input,
    884      test.instruction,
    885      "\t"
    886    );
    887    equal(text, test.expected, "output for " + test.desc);
    888 
    889    let expectChanged;
    890    if ("changed" in test) {
    891      expectChanged = test.changed;
    892    } else {
    893      expectChanged = {};
    894    }
    895    deepEqual(changed, expectChanged, "changed result for " + test.desc);
    896  }
    897 }