tor-browser

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

browser_misused_characters_in_strings.js (6795B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /* This list allows pre-existing or 'unfixable' issues to remain, while we
      5 * detect newly occurring issues in shipping files. It is a list of objects
      6 * specifying conditions under which an error should be ignored.
      7 *
      8 * As each issue is found in the exceptions list, it is removed from the list.
      9 * At the end of the test, there is an assertion that all items have been
     10 * removed from the exceptions list, thus ensuring there are no stale
     11 * entries. */
     12 let gExceptionsList = [
     13  {
     14    file: "layout_errors.properties",
     15    key: "ImageMapRectBoundsError",
     16    type: "double-quote",
     17  },
     18  {
     19    file: "layout_errors.properties",
     20    key: "ImageMapCircleWrongNumberOfCoords",
     21    type: "double-quote",
     22  },
     23  {
     24    file: "layout_errors.properties",
     25    key: "ImageMapCircleNegativeRadius",
     26    type: "double-quote",
     27  },
     28  {
     29    file: "layout_errors.properties",
     30    key: "ImageMapPolyWrongNumberOfCoords",
     31    type: "double-quote",
     32  },
     33  {
     34    file: "layout_errors.properties",
     35    key: "ImageMapPolyOddNumberOfCoords",
     36    type: "double-quote",
     37  },
     38  {
     39    file: "dom.properties",
     40    key: "ImportMapExternalNotSupported",
     41    type: "single-quote",
     42  },
     43  // dom.properties is packaged twice so we need to have two exceptions for this string.
     44  {
     45    file: "dom.properties",
     46    key: "ImportMapExternalNotSupported",
     47    type: "single-quote",
     48  },
     49  {
     50    file: "dom.properties",
     51    key: "MathML_DeprecatedMathVariantWarning",
     52    type: "single-quote",
     53  },
     54  // dom.properties is packaged twice so we need to have two exceptions for this string.
     55  {
     56    file: "dom.properties",
     57    key: "MathML_DeprecatedMathVariantWarning",
     58    type: "single-quote",
     59  },
     60  // These error messages contain references to the CSP keywords like 'unsafe-eval',
     61  // and those keywords contain actual single-quotes: https://w3c.github.io/webappsec-csp/#grammardef-keyword-source
     62  {
     63    file: "csp.properties",
     64    key: "CSPInlineStyleViolation2",
     65    type: "single-quote",
     66  },
     67  {
     68    file: "csp.properties",
     69    key: "CSPROInlineStyleViolation2",
     70    type: "single-quote",
     71  },
     72  {
     73    file: "csp.properties",
     74    key: "CSPInlineScriptViolation2",
     75    type: "single-quote",
     76  },
     77  {
     78    file: "csp.properties",
     79    key: "CSPROInlineScriptViolation2",
     80    type: "single-quote",
     81  },
     82  {
     83    file: "csp.properties",
     84    key: "CSPEventHandlerScriptViolation2",
     85    type: "single-quote",
     86  },
     87  {
     88    file: "csp.properties",
     89    key: "CSPROEventHandlerScriptViolation2",
     90    type: "single-quote",
     91  },
     92  {
     93    file: "csp.properties",
     94    key: "CSPEvalScriptViolation",
     95    type: "single-quote",
     96  },
     97  {
     98    file: "csp.properties",
     99    key: "CSPROEvalScriptViolation",
    100    type: "single-quote",
    101  },
    102  {
    103    file: "csp.properties",
    104    key: "CSPWasmEvalScriptViolation",
    105    type: "single-quote",
    106  },
    107  {
    108    file: "csp.properties",
    109    key: "CSPROWasmEvalScriptViolation",
    110    type: "single-quote",
    111  },
    112 ];
    113 
    114 /**
    115 * Check if an error should be ignored due to matching one of the exceptions
    116 * defined in gExceptionsList.
    117 *
    118 * @param filepath The URI spec of the locale file
    119 * @param key The key of the entity that is being checked
    120 * @param type The type of error that has been found
    121 * @return true if the error should be ignored, false otherwise.
    122 */
    123 function ignoredError(filepath, key, type) {
    124  for (let index in gExceptionsList) {
    125    let exceptionItem = gExceptionsList[index];
    126    if (
    127      filepath.endsWith(exceptionItem.file) &&
    128      key == exceptionItem.key &&
    129      type == exceptionItem.type
    130    ) {
    131      gExceptionsList.splice(index, 1);
    132      return true;
    133    }
    134  }
    135  return false;
    136 }
    137 
    138 function testForError(filepath, key, str, pattern, type, helpText) {
    139  if (str.match(pattern) && !ignoredError(filepath, key, type)) {
    140    ok(false, `${filepath} with key=${key} has a misused ${type}. ${helpText}`);
    141  }
    142 }
    143 
    144 function testForErrors(filepath, key, str) {
    145  testForError(
    146    filepath,
    147    key,
    148    str,
    149    /(\w|^)'\w/,
    150    "apostrophe",
    151    "Strings with apostrophes should use foo\u2019s instead of foo's."
    152  );
    153  testForError(
    154    filepath,
    155    key,
    156    str,
    157    /\w\u2018\w/,
    158    "incorrect-apostrophe",
    159    "Strings with apostrophes should use foo\u2019s instead of foo\u2018s."
    160  );
    161  testForError(
    162    filepath,
    163    key,
    164    str,
    165    /'.+'/,
    166    "single-quote",
    167    "Single-quoted strings should use Unicode \u2018foo\u2019 instead of 'foo'."
    168  );
    169  testForError(
    170    filepath,
    171    key,
    172    str,
    173    /"/,
    174    "double-quote",
    175    'Double-quoted strings should use Unicode \u201cfoo\u201d instead of "foo".'
    176  );
    177  testForError(
    178    filepath,
    179    key,
    180    str,
    181    /\.\.\./,
    182    "ellipsis",
    183    "Strings with an ellipsis should use the Unicode \u2026 character instead of three periods."
    184  );
    185 }
    186 
    187 async function getAllTheFiles(extension) {
    188  let appDirGreD = Services.dirsvc.get("GreD", Ci.nsIFile);
    189  let appDirXCurProcD = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
    190  if (appDirGreD.contains(appDirXCurProcD)) {
    191    return generateURIsFromDirTree(appDirGreD, [extension]);
    192  }
    193  if (appDirXCurProcD.contains(appDirGreD)) {
    194    return generateURIsFromDirTree(appDirXCurProcD, [extension]);
    195  }
    196  let urisGreD = await generateURIsFromDirTree(appDirGreD, [extension]);
    197  let urisXCurProcD = await generateURIsFromDirTree(appDirXCurProcD, [
    198    extension,
    199  ]);
    200  return Array.from(new Set(urisGreD.concat(urisXCurProcD)));
    201 }
    202 
    203 add_task(async function checkAllTheProperties() {
    204  // This asynchronously produces a list of URLs (sadly, mostly sync on our
    205  // test infrastructure because it runs against jarfiles there, and
    206  // our zipreader APIs are all sync)
    207  let uris = await getAllTheFiles(".properties");
    208  ok(
    209    uris.length,
    210    `Found ${uris.length} .properties files to scan for misused characters`
    211  );
    212 
    213  for (let uri of uris) {
    214    let bundle = Services.strings.createBundle(uri.spec);
    215 
    216    for (let entity of bundle.getSimpleEnumeration()) {
    217      testForErrors(uri.spec, entity.key, entity.value);
    218    }
    219  }
    220 });
    221 
    222 add_task(async function checkAllTheFluents() {
    223  let uris = await getAllTheFiles(".ftl");
    224 
    225  let domParser = new DOMParser();
    226  domParser.forceEnableDTD();
    227 
    228  for (let uri of uris) {
    229    let rawContents = await fetchFile(uri.spec);
    230    const resource = new FluentResource(rawContents);
    231 
    232    for (const info of resource.textElements()) {
    233      const key = info.attr ? `${info.id}.${info.attr}` : info.id;
    234 
    235      const stripped_val = domParser.parseFromString(
    236        "<!DOCTYPE html>" + info.text,
    237        "text/html"
    238      ).documentElement.textContent;
    239 
    240      testForErrors(uri.spec, key, stripped_val);
    241    }
    242  }
    243 });
    244 
    245 add_task(async function ensureExceptionsListIsEmpty() {
    246  is(gExceptionsList.length, 0, "No remaining exceptions exist");
    247 });