From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser_misused_characters_in_strings.js | 276 +++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 browser/base/content/test/static/browser_misused_characters_in_strings.js (limited to 'browser/base/content/test/static/browser_misused_characters_in_strings.js') diff --git a/browser/base/content/test/static/browser_misused_characters_in_strings.js b/browser/base/content/test/static/browser_misused_characters_in_strings.js new file mode 100644 index 0000000000..42be3b4392 --- /dev/null +++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js @@ -0,0 +1,276 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* This list allows pre-existing or 'unfixable' issues to remain, while we + * detect newly occurring issues in shipping files. It is a list of objects + * specifying conditions under which an error should be ignored. + * + * As each issue is found in the exceptions list, it is removed from the list. + * At the end of the test, there is an assertion that all items have been + * removed from the exceptions list, thus ensuring there are no stale + * entries. */ +let gExceptionsList = [ + { + file: "layout_errors.properties", + key: "ImageMapRectBoundsError", + type: "double-quote", + }, + { + file: "layout_errors.properties", + key: "ImageMapCircleWrongNumberOfCoords", + type: "double-quote", + }, + { + file: "layout_errors.properties", + key: "ImageMapCircleNegativeRadius", + type: "double-quote", + }, + { + file: "layout_errors.properties", + key: "ImageMapPolyWrongNumberOfCoords", + type: "double-quote", + }, + { + file: "layout_errors.properties", + key: "ImageMapPolyOddNumberOfCoords", + type: "double-quote", + }, + { + file: "dom.properties", + key: "PatternAttributeCompileFailure", + type: "single-quote", + }, + // dom.properties is packaged twice so we need to have two exceptions for this string. + { + file: "dom.properties", + key: "PatternAttributeCompileFailure", + type: "single-quote", + }, + { + file: "dom.properties", + key: "ImportMapExternalNotSupported", + type: "single-quote", + }, + // dom.properties is packaged twice so we need to have two exceptions for this string. + { + file: "dom.properties", + key: "ImportMapExternalNotSupported", + type: "single-quote", + }, +]; + +/** + * Check if an error should be ignored due to matching one of the exceptions + * defined in gExceptionsList. + * + * @param filepath The URI spec of the locale file + * @param key The key of the entity that is being checked + * @param type The type of error that has been found + * @return true if the error should be ignored, false otherwise. + */ +function ignoredError(filepath, key, type) { + for (let index in gExceptionsList) { + let exceptionItem = gExceptionsList[index]; + if ( + filepath.endsWith(exceptionItem.file) && + key == exceptionItem.key && + type == exceptionItem.type + ) { + gExceptionsList.splice(index, 1); + return true; + } + } + return false; +} + +function testForError(filepath, key, str, pattern, type, helpText) { + if (str.match(pattern) && !ignoredError(filepath, key, type)) { + ok(false, `${filepath} with key=${key} has a misused ${type}. ${helpText}`); + } +} + +function testForErrors(filepath, key, str) { + testForError( + filepath, + key, + str, + /(\w|^)'\w/, + "apostrophe", + "Strings with apostrophes should use foo\u2019s instead of foo's." + ); + testForError( + filepath, + key, + str, + /\w\u2018\w/, + "incorrect-apostrophe", + "Strings with apostrophes should use foo\u2019s instead of foo\u2018s." + ); + testForError( + filepath, + key, + str, + /'.+'/, + "single-quote", + "Single-quoted strings should use Unicode \u2018foo\u2019 instead of 'foo'." + ); + testForError( + filepath, + key, + str, + /"/, + "double-quote", + 'Double-quoted strings should use Unicode \u201cfoo\u201d instead of "foo".' + ); + testForError( + filepath, + key, + str, + /\.\.\./, + "ellipsis", + "Strings with an ellipsis should use the Unicode \u2026 character instead of three periods." + ); +} + +async function getAllTheFiles(extension) { + let appDirGreD = Services.dirsvc.get("GreD", Ci.nsIFile); + let appDirXCurProcD = Services.dirsvc.get("XCurProcD", Ci.nsIFile); + if (appDirGreD.contains(appDirXCurProcD)) { + return generateURIsFromDirTree(appDirGreD, [extension]); + } + if (appDirXCurProcD.contains(appDirGreD)) { + return generateURIsFromDirTree(appDirXCurProcD, [extension]); + } + let urisGreD = await generateURIsFromDirTree(appDirGreD, [extension]); + let urisXCurProcD = await generateURIsFromDirTree(appDirXCurProcD, [ + extension, + ]); + return Array.from(new Set(urisGreD.concat(urisXCurProcD))); +} + +add_task(async function checkAllTheProperties() { + // This asynchronously produces a list of URLs (sadly, mostly sync on our + // test infrastructure because it runs against jarfiles there, and + // our zipreader APIs are all sync) + let uris = await getAllTheFiles(".properties"); + ok( + uris.length, + `Found ${uris.length} .properties files to scan for misused characters` + ); + + for (let uri of uris) { + let bundle = Services.strings.createBundle(uri.spec); + + for (let entity of bundle.getSimpleEnumeration()) { + testForErrors(uri.spec, entity.key, entity.value); + } + } +}); + +var checkDTD = async function (aURISpec) { + let rawContents = await fetchFile(aURISpec); + // The regular expression below is adapted from: + // https://hg.mozilla.org/mozilla-central/file/68c0b7d6f16ce5bb023e08050102b5f2fe4aacd8/python/compare-locales/compare_locales/parser.py#l233 + let entities = rawContents.match( + //g + ); + if (!entities) { + // Some files have no entities defined. + return; + } + for (let entity of entities) { + let [, key, str] = entity.match( + // + ); + // The matched string includes the enclosing quotation marks, + // we need to slice them off. + str = str.slice(1, -1); + testForErrors(aURISpec, key, str); + } +}; + +add_task(async function checkAllTheDTDs() { + let uris = await getAllTheFiles(".dtd"); + ok( + uris.length, + `Found ${uris.length} .dtd files to scan for misused characters` + ); + for (let uri of uris) { + await checkDTD(uri.spec); + } + + // This support DTD file supplies a string with a newline to make sure + // the regex in checkDTD works correctly for that case. + let dtdLocation = gTestPath.replace( + /\/[^\/]*$/i, + "/bug1262648_string_with_newlines.dtd" + ); + await checkDTD(dtdLocation); +}); + +add_task(async function checkAllTheFluents() { + let uris = await getAllTheFiles(".ftl"); + let { FluentParser, Visitor } = ChromeUtils.import( + "resource://testing-common/FluentSyntax.jsm" + ); + + class TextElementVisitor extends Visitor { + constructor() { + super(); + let domParser = new DOMParser(); + domParser.forceEnableDTD(); + + this.domParser = domParser; + this.uri = null; + this.id = null; + this.attr = null; + } + + visitMessage(node) { + this.id = node.id.name; + this.attr = null; + this.genericVisit(node); + } + + visitTerm(node) { + this.id = node.id.name; + this.attr = null; + this.genericVisit(node); + } + + visitAttribute(node) { + this.attr = node.id.name; + this.genericVisit(node); + } + + get key() { + if (this.attr) { + return `${this.id}.${this.attr}`; + } + return this.id; + } + + visitTextElement(node) { + const stripped_val = this.domParser.parseFromString( + "" + node.value, + "text/html" + ).documentElement.textContent; + testForErrors(this.uri, this.key, stripped_val); + } + } + + const ftlParser = new FluentParser({ withSpans: false }); + const visitor = new TextElementVisitor(); + + for (let uri of uris) { + let rawContents = await fetchFile(uri.spec); + let ast = ftlParser.parse(rawContents); + + visitor.uri = uri.spec; + visitor.visit(ast); + } +}); + +add_task(async function ensureExceptionsListIsEmpty() { + is(gExceptionsList.length, 0, "No remaining exceptions exist"); +}); -- cgit v1.2.3