Bug 1561435 - Format tools/, a=automatic-formatting

# ignore-this-changeset

Differential Revision: https://phabricator.services.mozilla.com/D35940

--HG--
extra : source : d214f0c82813e5a8d3987debc490a2c11f1308ff
This commit is contained in:
Victor Porof 2019-07-05 11:18:19 +02:00
parent 336c4fb8a4
commit 5c7cdbd4ba
88 changed files with 2499 additions and 1910 deletions

View file

@ -45,7 +45,6 @@ module.exports = {
"overrides": [{ "overrides": [{
"files": [ "files": [
"devtools/**", "devtools/**",
"tools/**",
"uriloader/**", "uriloader/**",
"view/**", "view/**",
"widget/**", "widget/**",

View file

@ -40,7 +40,6 @@ toolkit/components/telemetry/datareporting-prefs.js
toolkit/components/telemetry/healthreport-prefs.js toolkit/components/telemetry/healthreport-prefs.js
# Ignore all top-level directories for now. # Ignore all top-level directories for now.
tools/**
uriloader/** uriloader/**
view/** view/**
widget/** widget/**

View file

@ -8,9 +8,11 @@
var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"]; var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); const env = Cc["@mozilla.org/process/environment;1"].getService(
Ci.nsIEnvironment
);
// This is the directory where gcov is emitting the gcda files. // This is the directory where gcov is emitting the gcda files.
const gcovPrefixPath = env.get("GCOV_PREFIX"); const gcovPrefixPath = env.get("GCOV_PREFIX");
// This is the directory where codecoverage.py is expecting to see the gcda files. // This is the directory where codecoverage.py is expecting to see the gcda files.
@ -20,7 +22,9 @@ const jsvmPrefixPath = env.get("JS_CODE_COVERAGE_OUTPUT_DIR");
// This is the directory where codecoverage.py is expecting to see the lcov files. // This is the directory where codecoverage.py is expecting to see the lcov files.
const jsvmResultsPath = env.get("JSVM_RESULTS_DIR"); const jsvmResultsPath = env.get("JSVM_RESULTS_DIR");
const gcovPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); const gcovPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
);
if (gcovPrefixPath) { if (gcovPrefixPath) {
gcovPrefixDir.initWithPath(gcovPrefixPath); gcovPrefixDir.initWithPath(gcovPrefixPath);
} }
@ -30,7 +34,9 @@ if (gcovResultsPath) {
gcovResultsDir.initWithPath(gcovResultsPath); gcovResultsDir.initWithPath(gcovResultsPath);
} }
const jsvmPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); const jsvmPrefixDir = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
);
if (jsvmPrefixPath) { if (jsvmPrefixPath) {
jsvmPrefixDir.initWithPath(jsvmPrefixPath); jsvmPrefixDir.initWithPath(jsvmPrefixPath);
} }
@ -44,10 +50,12 @@ function awaitPromise(promise) {
let ret; let ret;
let complete = false; let complete = false;
let error = null; let error = null;
promise.catch(e => error = e).then(v => { promise
ret = v; .catch(e => (error = e))
complete = true; .then(v => {
}); ret = v;
complete = true;
});
Services.tm.spinEventLoopUntil(() => complete); Services.tm.spinEventLoopUntil(() => complete);
if (error) { if (error) {
throw new Error(error); throw new Error(error);
@ -77,7 +85,9 @@ var PerTestCoverageUtils = class PerTestCoverageUtilsClass {
} }
// Flush the counters. // Flush the counters.
let codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); let codeCoverageService = Cc[
"@mozilla.org/tools/code-coverage;1"
].getService(Ci.nsICodeCoverage);
await codeCoverageService.flushCounters(); await codeCoverageService.flushCounters();
// Remove coverage files created by the flush, and those that might have been created between the end of a previous test and the beginning of the next one (e.g. some tests can create a new content process for every sub-test). // Remove coverage files created by the flush, and those that might have been created between the end of a previous test and the beginning of the next one (e.g. some tests can create a new content process for every sub-test).
@ -100,7 +110,9 @@ var PerTestCoverageUtils = class PerTestCoverageUtilsClass {
} }
// Flush the counters. // Flush the counters.
let codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); let codeCoverageService = Cc[
"@mozilla.org/tools/code-coverage;1"
].getService(Ci.nsICodeCoverage);
await codeCoverageService.flushCounters(); await codeCoverageService.flushCounters();
// Move the coverage files in GCOV_RESULTS_DIR and JSVM_RESULTS_DIR, so that the execution from now to shutdown (or next test) is not counted. // Move the coverage files in GCOV_RESULTS_DIR and JSVM_RESULTS_DIR, so that the execution from now to shutdown (or next test) is not counted.

View file

@ -2,15 +2,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); var { AppConstants } = ChromeUtils.import(
var {OS, require} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); "resource://gre/modules/AppConstants.jsm"
);
var { OS, require } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
function getFiles() { function getFiles() {
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); const env = Cc["@mozilla.org/process/environment;1"].getService(
Ci.nsIEnvironment
);
// This is the directory where gcov is emitting the gcda files. // This is the directory where gcov is emitting the gcda files.
const jsCoveragePath = env.get("JS_CODE_COVERAGE_OUTPUT_DIR"); const jsCoveragePath = env.get("JS_CODE_COVERAGE_OUTPUT_DIR");
const jsCoverageDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); const jsCoverageDir = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
);
jsCoverageDir.initWithPath(jsCoveragePath); jsCoverageDir.initWithPath(jsCoveragePath);
let files = []; let files = [];
@ -47,9 +53,9 @@ function parseRecords(files) {
let [hits, name] = recordContent.split(","); let [hits, name] = recordContent.split(",");
currentSF.push({ currentSF.push({
"type": "FNDA", type: "FNDA",
"hits": hits, hits,
"name": name, name,
}); });
break; break;
} }
@ -61,8 +67,8 @@ function parseRecords(files) {
let name = recordContent.split(",")[1]; let name = recordContent.split(",")[1];
currentSF.push({ currentSF.push({
"type": "FN", type: "FN",
"name": name, name,
}); });
break; break;
} }

View file

@ -29,19 +29,41 @@ async function run_test() {
await codeCoverage.flushCounters(); await codeCoverage.flushCounters();
const first_flush_files = getFiles(); const first_flush_files = getFiles();
const first_flush_records = parseRecords(diffFiles(first_flush_files, files_orig)); const first_flush_records = parseRecords(
diffFiles(first_flush_files, files_orig)
);
Assert.ok(first_flush_records.has("test_basic.js")); Assert.ok(first_flush_records.has("test_basic.js"));
let fnRecords = first_flush_records.get("test_basic.js").filter(record => record.type == "FN"); let fnRecords = first_flush_records
let fndaRecords = first_flush_records.get("test_basic.js").filter(record => record.type == "FNDA"); .get("test_basic.js")
.filter(record => record.type == "FN");
let fndaRecords = first_flush_records
.get("test_basic.js")
.filter(record => record.type == "FNDA");
Assert.ok(fnRecords.some(record => record.name == "top-level")); Assert.ok(fnRecords.some(record => record.name == "top-level"));
Assert.ok(fnRecords.some(record => record.name == "run_test")); Assert.ok(fnRecords.some(record => record.name == "run_test"));
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func1")); Assert.ok(
Assert.ok(fndaRecords.some(record => record.name == "run_test" && record.hits == 1)); fnRecords.some(record => record.name == "test_code_coverage_func1")
Assert.ok(!fndaRecords.some(record => record.name == "run_test" && record.hits != 1)); );
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits == 1)); Assert.ok(
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits != 1)); fndaRecords.some(record => record.name == "run_test" && record.hits == 1)
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func2")); );
Assert.ok(
!fndaRecords.some(record => record.name == "run_test" && record.hits != 1)
);
Assert.ok(
fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits == 1
)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits != 1
)
);
Assert.ok(
!fndaRecords.some(record => record.name == "test_code_coverage_func2")
);
test_code_coverage_func2(); test_code_coverage_func2();
@ -49,19 +71,45 @@ async function run_test() {
await codeCoverage.flushCounters(); await codeCoverage.flushCounters();
const second_flush_files = getFiles(); const second_flush_files = getFiles();
const second_flush_records = parseRecords(diffFiles(second_flush_files, first_flush_files)); const second_flush_records = parseRecords(
diffFiles(second_flush_files, first_flush_files)
);
Assert.ok(second_flush_records.has("test_basic.js")); Assert.ok(second_flush_records.has("test_basic.js"));
fnRecords = second_flush_records.get("test_basic.js").filter(record => record.type == "FN"); fnRecords = second_flush_records
fndaRecords = second_flush_records.get("test_basic.js").filter(record => record.type == "FNDA"); .get("test_basic.js")
.filter(record => record.type == "FN");
fndaRecords = second_flush_records
.get("test_basic.js")
.filter(record => record.type == "FNDA");
Assert.ok(fnRecords.some(record => record.name == "top-level")); Assert.ok(fnRecords.some(record => record.name == "top-level"));
Assert.ok(fnRecords.some(record => record.name == "run_test")); Assert.ok(fnRecords.some(record => record.name == "run_test"));
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func1")); Assert.ok(
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func2")); fnRecords.some(record => record.name == "test_code_coverage_func1")
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits == 0)); );
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits != 0)); Assert.ok(
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func2" && record.hits == 1)); fnRecords.some(record => record.name == "test_code_coverage_func2")
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func2" && record.hits != 1)); );
Assert.ok(
fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits == 0
)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits != 0
)
);
Assert.ok(
fndaRecords.some(
record => record.name == "test_code_coverage_func2" && record.hits == 1
)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func2" && record.hits != 1
)
);
do_test_finished(); do_test_finished();
} }

View file

@ -10,7 +10,9 @@ async function run_test() {
do_load_child_test_harness(); do_load_child_test_harness();
do_test_pending(); do_test_pending();
const codeCoverage = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); const codeCoverage = Cc["@mozilla.org/tools/code-coverage;1"].getService(
Ci.nsICodeCoverage
);
const files_orig = getFiles(); const files_orig = getFiles();
@ -19,44 +21,100 @@ async function run_test() {
await codeCoverage.flushCounters(); await codeCoverage.flushCounters();
const first_flush_files = getFiles(); const first_flush_files = getFiles();
const first_flush_records = parseRecords(diffFiles(first_flush_files, files_orig)); const first_flush_records = parseRecords(
diffFiles(first_flush_files, files_orig)
);
Assert.ok(first_flush_records.has("test_basic_child_and_parent.js")); Assert.ok(first_flush_records.has("test_basic_child_and_parent.js"));
Assert.ok(!first_flush_records.has("support.js")); Assert.ok(!first_flush_records.has("support.js"));
let fnRecords = first_flush_records.get("test_basic_child_and_parent.js").filter(record => record.type == "FN"); let fnRecords = first_flush_records
let fndaRecords = first_flush_records.get("test_basic_child_and_parent.js").filter(record => record.type == "FNDA"); .get("test_basic_child_and_parent.js")
.filter(record => record.type == "FN");
let fndaRecords = first_flush_records
.get("test_basic_child_and_parent.js")
.filter(record => record.type == "FNDA");
Assert.ok(fnRecords.some(record => record.name == "top-level")); Assert.ok(fnRecords.some(record => record.name == "top-level"));
Assert.ok(fnRecords.some(record => record.name == "run_test")); Assert.ok(fnRecords.some(record => record.name == "run_test"));
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func1")); Assert.ok(
Assert.ok(fndaRecords.some(record => record.name == "run_test" && record.hits == 1)); fnRecords.some(record => record.name == "test_code_coverage_func1")
Assert.ok(!fndaRecords.some(record => record.name == "run_test" && record.hits != 1)); );
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits == 1)); Assert.ok(
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits != 1)); fndaRecords.some(record => record.name == "run_test" && record.hits == 1)
);
Assert.ok(
!fndaRecords.some(record => record.name == "run_test" && record.hits != 1)
);
Assert.ok(
fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits == 1
)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits != 1
)
);
sendCommand("load('support.js');", async function() { sendCommand("load('support.js');", async function() {
await codeCoverage.flushCounters(); await codeCoverage.flushCounters();
const second_flush_files = getFiles(); const second_flush_files = getFiles();
const second_flush_records = parseRecords(diffFiles(second_flush_files, first_flush_files)); const second_flush_records = parseRecords(
diffFiles(second_flush_files, first_flush_files)
);
Assert.ok(second_flush_records.has("test_basic_child_and_parent.js")); Assert.ok(second_flush_records.has("test_basic_child_and_parent.js"));
fnRecords = second_flush_records.get("test_basic_child_and_parent.js").filter(record => record.type == "FN"); fnRecords = second_flush_records
fndaRecords = second_flush_records.get("test_basic_child_and_parent.js").filter(record => record.type == "FNDA"); .get("test_basic_child_and_parent.js")
Assert.ok(fnRecords.some(record => record.name == "top-level")); .filter(record => record.type == "FN");
Assert.ok(fnRecords.some(record => record.name == "run_test")); fndaRecords = second_flush_records
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func1")); .get("test_basic_child_and_parent.js")
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits == 0)); .filter(record => record.type == "FNDA");
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func1" && record.hits != 0)); Assert.ok(fnRecords.some(record => record.name == "top-level"));
Assert.ok(second_flush_records.has("support.js")); Assert.ok(fnRecords.some(record => record.name == "run_test"));
fnRecords = second_flush_records.get("support.js").filter(record => record.type == "FN"); Assert.ok(
fndaRecords = second_flush_records.get("support.js").filter(record => record.type == "FNDA"); fnRecords.some(record => record.name == "test_code_coverage_func1")
Assert.ok(fnRecords.some(record => record.name == "top-level")); );
Assert.ok(fnRecords.some(record => record.name == "test_code_coverage_func2")); Assert.ok(
Assert.ok(fndaRecords.some(record => record.name == "top-level" && record.hits == 1)); fndaRecords.some(
Assert.ok(!fndaRecords.some(record => record.name == "top-level" && record.hits != 1)); record => record.name == "test_code_coverage_func1" && record.hits == 0
Assert.ok(fndaRecords.some(record => record.name == "test_code_coverage_func2" && record.hits == 1)); )
Assert.ok(!fndaRecords.some(record => record.name == "test_code_coverage_func2" && record.hits != 1)); );
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func1" && record.hits != 0
)
);
Assert.ok(second_flush_records.has("support.js"));
fnRecords = second_flush_records
.get("support.js")
.filter(record => record.type == "FN");
fndaRecords = second_flush_records
.get("support.js")
.filter(record => record.type == "FNDA");
Assert.ok(fnRecords.some(record => record.name == "top-level"));
Assert.ok(
fnRecords.some(record => record.name == "test_code_coverage_func2")
);
Assert.ok(
fndaRecords.some(record => record.name == "top-level" && record.hits == 1)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "top-level" && record.hits != 1
)
);
Assert.ok(
fndaRecords.some(
record => record.name == "test_code_coverage_func2" && record.hits == 1
)
);
Assert.ok(
!fndaRecords.some(
record => record.name == "test_code_coverage_func2" && record.hits != 1
)
);
do_test_finished(); do_test_finished();
}); });
} }

View file

@ -2,64 +2,62 @@
"use strict"; "use strict";
module.exports = { module.exports = {
"env": { env: {
"browser": true, browser: true,
"mozilla/browser-window": true, "mozilla/browser-window": true,
"mozilla/simpletest": true, "mozilla/simpletest": true,
// "node": true // "node": true
}, },
// All globals made available in the test environment. // All globals made available in the test environment.
"globals": { globals: {
// `$` is defined in SimpleTest.js // `$` is defined in SimpleTest.js
"$": false, $: false,
"Assert": false, Assert: false,
"BrowserTestUtils": false, BrowserTestUtils: false,
"ContentTask": false, ContentTask: false,
"ContentTaskUtils": false, ContentTaskUtils: false,
"EventUtils": false, EventUtils: false,
"PromiseDebugging": false, PromiseDebugging: false,
"SpecialPowers": false, SpecialPowers: false,
"TestUtils": false, TestUtils: false,
"XPCNativeWrapper": false, XPCNativeWrapper: false,
"XULDocument": false, XULDocument: false,
"addLoadEvent": false, addLoadEvent: false,
"add_task": false, add_task: false,
"content": false, content: false,
"executeSoon": false, executeSoon: false,
"expectUncaughtException": false, expectUncaughtException: false,
"export_assertions": false, export_assertions: false,
"extractJarToTmp": false, extractJarToTmp: false,
"finish": false, finish: false,
"gTestPath": false, gTestPath: false,
"getChromeDir": false, getChromeDir: false,
"getJar": false, getJar: false,
"getResolvedURI": false, getResolvedURI: false,
"getRootDirectory": false, getRootDirectory: false,
"getTestFilePath": false, getTestFilePath: false,
"ignoreAllUncaughtExceptions": false, ignoreAllUncaughtExceptions: false,
"info": false, info: false,
"is": false, is: false,
"isnot": false, isnot: false,
"ok": false, ok: false,
"privateNoteIntentionalCrash": false, privateNoteIntentionalCrash: false,
"record": false, record: false,
"registerCleanupFunction": false, registerCleanupFunction: false,
"requestLongerTimeout": false, requestLongerTimeout: false,
"setExpectedFailuresForSelfTest": false, setExpectedFailuresForSelfTest: false,
"todo": false, todo: false,
"todo_is": false, todo_is: false,
"todo_isnot": false, todo_isnot: false,
"waitForClipboard": false, waitForClipboard: false,
"waitForExplicitFinish": false, waitForExplicitFinish: false,
"waitForFocus": false, waitForFocus: false,
}, },
"plugins": [ plugins: ["mozilla"],
"mozilla",
],
"rules": { rules: {
"mozilla/import-content-task-globals": "error", "mozilla/import-content-task-globals": "error",
"mozilla/import-headjs-globals": "error", "mozilla/import-headjs-globals": "error",
"mozilla/mark-test-function-used": "error", "mozilla/mark-test-function-used": "error",

View file

@ -2,31 +2,31 @@
"use strict"; "use strict";
module.exports = { module.exports = {
"env": { env: {
"browser": true, browser: true,
"mozilla/browser-window": true, "mozilla/browser-window": true,
}, },
// All globals made available in the test environment. // All globals made available in the test environment.
"globals": { globals: {
// SpecialPowers is injected into the window object via SimpleTest.js // SpecialPowers is injected into the window object via SimpleTest.js
"SpecialPowers": false, SpecialPowers: false,
}, },
"overrides": [{ overrides: [
"env": { {
// Ideally we wouldn't be using the simpletest env here, but our uses of env: {
// js files mean we pick up everything from the global scope, which could // Ideally we wouldn't be using the simpletest env here, but our uses of
// be any one of a number of html files. So we just allow the basics... // js files mean we pick up everything from the global scope, which could
"mozilla/simpletest": true, // be any one of a number of html files. So we just allow the basics...
"mozilla/simpletest": true,
},
files: ["*.js"],
}, },
"files": ["*.js"],
}],
"plugins": [
"mozilla",
], ],
plugins: ["mozilla"],
rules: { rules: {
"mozilla/import-content-task-globals": "error", "mozilla/import-content-task-globals": "error",
"mozilla/import-headjs-globals": "error", "mozilla/import-headjs-globals": "error",

View file

@ -2,32 +2,32 @@
"use strict"; "use strict";
module.exports = { module.exports = {
"env": { env: {
"browser": true, browser: true,
}, },
// All globals made available in the test environment. // All globals made available in the test environment.
"globals": { globals: {
// SpecialPowers is injected into the window object via SimpleTest.js // SpecialPowers is injected into the window object via SimpleTest.js
"SpecialPowers": false, SpecialPowers: false,
"XPCNativeWrapper": false, XPCNativeWrapper: false,
}, },
"overrides": [{ overrides: [
"env": { {
// Ideally we wouldn't be using the simpletest env here, but our uses of env: {
// js files mean we pick up everything from the global scope, which could // Ideally we wouldn't be using the simpletest env here, but our uses of
// be any one of a number of html files. So we just allow the basics... // js files mean we pick up everything from the global scope, which could
"mozilla/simpletest": true, // be any one of a number of html files. So we just allow the basics...
"mozilla/simpletest": true,
},
files: ["*.js"],
}, },
"files": ["*.js"],
}],
"plugins": [
"mozilla",
], ],
"rules": { plugins: ["mozilla"],
rules: {
"mozilla/import-content-task-globals": "error", "mozilla/import-content-task-globals": "error",
"mozilla/import-headjs-globals": "error", "mozilla/import-headjs-globals": "error",
"mozilla/mark-test-function-used": "error", "mozilla/mark-test-function-used": "error",

View file

@ -8,89 +8,88 @@
* https://eslint.org/docs/rules/ * https://eslint.org/docs/rules/
*/ */
module.exports = { module.exports = {
"env": { env: {
"browser": true, browser: true,
"es6": true, es6: true,
"mozilla/privileged": true, "mozilla/privileged": true,
}, },
"extends": [ extends: ["eslint:recommended", "plugin:prettier/recommended"],
"eslint:recommended",
"plugin:prettier/recommended",
],
"globals": { globals: {
"Cc": false, Cc: false,
// Specific to Firefox (Chrome code only). // Specific to Firefox (Chrome code only).
"ChromeUtils": false, ChromeUtils: false,
"Ci": false, Ci: false,
"Components": false, Components: false,
"Cr": false, Cr: false,
"Cu": false, Cu: false,
"Debugger": false, Debugger: false,
"InstallTrigger": false, InstallTrigger: false,
// Specific to Firefox // Specific to Firefox
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/InternalError // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/InternalError
"InternalError": true, InternalError: true,
"Intl": false, Intl: false,
"SharedArrayBuffer": false, SharedArrayBuffer: false,
"StopIteration": false, StopIteration: false,
"dump": true, dump: true,
// Override the "browser" env definition of "location" to allow writing as it // Override the "browser" env definition of "location" to allow writing as it
// is a writeable property. // is a writeable property.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1509270#c1 for more information. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1509270#c1 for more information.
"location": true, location: true,
"openDialog": false, openDialog: false,
"saveStack": false, saveStack: false,
"sizeToContent": false, sizeToContent: false,
// Specific to Firefox // Specific to Firefox
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval
"uneval": false, uneval: false,
}, },
"overrides": [{ overrides: [
// Turn off use-services for xml files. XBL bindings are going away, and {
// working out the valid globals for those is difficult. // Turn off use-services for xml files. XBL bindings are going away, and
"files": "**/*.xml", // working out the valid globals for those is difficult.
"rules": { files: "**/*.xml",
"mozilla/use-services": "off", rules: {
"mozilla/use-services": "off",
},
}, },
}, { {
// We don't have the general browser environment for jsm files, but we do // We don't have the general browser environment for jsm files, but we do
// have our own special environments for them. // have our own special environments for them.
"env": { env: {
"browser": false, browser: false,
"mozilla/jsm": true, "mozilla/jsm": true,
},
files: "**/*.jsm",
rules: {
"mozilla/mark-exported-symbols-as-used": "error",
// JSM modules are far easier to check for no-unused-vars on a global scope,
// than our content files. Hence we turn that on here.
"no-unused-vars": [
"error",
{
args: "none",
vars: "all",
},
],
},
}, },
"files": "**/*.jsm", ],
"rules": {
"mozilla/mark-exported-symbols-as-used": "error",
// JSM modules are far easier to check for no-unused-vars on a global scope,
// than our content files. Hence we turn that on here.
"no-unused-vars": ["error", {
"args": "none",
"vars": "all",
}],
},
}],
"parserOptions": { parserOptions: {
"ecmaVersion": 9, ecmaVersion: 9,
}, },
// When adding items to this file please check for effects on sub-directories. // When adding items to this file please check for effects on sub-directories.
"plugins": [ plugins: ["html", "fetch-options", "no-unsanitized"],
"html",
"fetch-options",
"no-unsanitized",
],
// When adding items to this file please check for effects on all of toolkit // When adding items to this file please check for effects on all of toolkit
// and browser // and browser
"rules": { rules: {
// Warn about cyclomatic complexity in functions. // Warn about cyclomatic complexity in functions.
// XXX Get this down to 20? // XXX Get this down to 20?
"complexity": ["error", 34], complexity: ["error", 34],
// Functions must always return something or nothing // Functions must always return something or nothing
"consistent-return": "error", "consistent-return": "error",
@ -100,7 +99,7 @@ module.exports = {
"constructor-super": "off", "constructor-super": "off",
// Require braces around blocks that start a new line // Require braces around blocks that start a new line
"curly": ["error", "all"], curly: ["error", "all"],
// Encourage the use of dot notation whenever possible. // Encourage the use of dot notation whenever possible.
"dot-notation": "error", "dot-notation": "error",
@ -164,7 +163,7 @@ module.exports = {
"no-else-return": "error", "no-else-return": "error",
// No empty statements // No empty statements
"no-empty": ["error", {"allowEmptyCatch": true}], "no-empty": ["error", { allowEmptyCatch: true }],
// Disallow eval and setInteral/setTimeout with strings // Disallow eval and setInteral/setTimeout with strings
"no-eval": "error", "no-eval": "error",
@ -189,7 +188,7 @@ module.exports = {
// Disallow the use of the __iterator__ property // Disallow the use of the __iterator__ property
"no-iterator": "error", "no-iterator": "error",
// No labels // No labels
"no-labels": "error", "no-labels": "error",
// Disallow unnecessary nested blocks // Disallow unnecessary nested blocks
@ -238,10 +237,13 @@ module.exports = {
"no-unsanitized/property": "error", "no-unsanitized/property": "error",
// No declaring variables that are never used // No declaring variables that are never used
"no-unused-vars": ["error", { "no-unused-vars": [
"args": "none", "error",
"vars": "local", {
}], args: "none",
vars: "local",
},
],
// No using variables before defined // No using variables before defined
// "no-use-before-define": ["error", "nofunc"], // "no-use-before-define": ["error", "nofunc"],
@ -264,7 +266,7 @@ module.exports = {
"no-with": "error", "no-with": "error",
// Require object-literal shorthand with ES6 method syntax // Require object-literal shorthand with ES6 method syntax
"object-shorthand": ["error", "always", { "avoidQuotes": true }], "object-shorthand": ["error", "always", { avoidQuotes: true }],
// XXX Bug 1487642 - decide if we want to enable this or not. // XXX Bug 1487642 - decide if we want to enable this or not.
// Require generator functions to contain yield // Require generator functions to contain yield
@ -274,7 +276,7 @@ module.exports = {
// To avoid bad interactions of the html plugin with the xml preprocessor in // To avoid bad interactions of the html plugin with the xml preprocessor in
// eslint-plugin-mozilla, we turn off processing of the html plugin for .xml // eslint-plugin-mozilla, we turn off processing of the html plugin for .xml
// files. // files.
"settings": { settings: {
"html/xml-extensions": [ ".xhtml" ], "html/xml-extensions": [".xhtml"],
}, },
}; };

View file

@ -3,80 +3,85 @@
module.exports = { module.exports = {
// All globals made available in the test environment. // All globals made available in the test environment.
"globals": { globals: {
"Assert": false, Assert: false,
"PromiseDebugging": false, PromiseDebugging: false,
"_TEST_FILE": false, _TEST_FILE: false,
"add_task": false, add_task: false,
"add_test": false, add_test: false,
// Test-only function. // Test-only function.
"allocationMarker": false, allocationMarker: false,
"byteSize": false, byteSize: false,
"deepEqual": false, deepEqual: false,
"do_await_remote_message": false, do_await_remote_message: false,
"do_check_instanceof": false, do_check_instanceof: false,
"do_get_cwd": false, do_get_cwd: false,
"do_get_file": false, do_get_file: false,
"do_get_idle": false, do_get_idle: false,
"do_get_profile": false, do_get_profile: false,
"do_get_tempdir": false, do_get_tempdir: false,
"do_load_child_test_harness": false, do_load_child_test_harness: false,
"do_load_manifest": false, do_load_manifest: false,
"do_load_module": false, do_load_module: false,
"do_note_exception": false, do_note_exception: false,
"do_parse_document": false, do_parse_document: false,
"do_report_unexpected_exception": false, do_report_unexpected_exception: false,
"do_send_remote_message": false, do_send_remote_message: false,
"do_test_finished": false, do_test_finished: false,
"do_test_pending": false, do_test_pending: false,
"do_throw": false, do_throw: false,
"do_timeout": false, do_timeout: false,
"equal": false, equal: false,
"executeSoon": false, executeSoon: false,
"gc": false, gc: false,
// XPCShell specific function, see XPCShellEnvironment.cpp // XPCShell specific function, see XPCShellEnvironment.cpp
"gczeal": false, gczeal: false,
"greater": false, greater: false,
"greaterOrEqual": false, greaterOrEqual: false,
"info": false, info: false,
"less": false, less: false,
"lessOrEqual": false, lessOrEqual: false,
"load": false, load: false,
"mozinfo": false, mozinfo: false,
"notDeepEqual": false, notDeepEqual: false,
"notEqual": false, notEqual: false,
"notStrictEqual": false, notStrictEqual: false,
"ok": false, ok: false,
"registerCleanupFunction": false, registerCleanupFunction: false,
"run_next_test": false, run_next_test: false,
"run_test": false, run_test: false,
"run_test_in_child": false, run_test_in_child: false,
"runningInParent": false, runningInParent: false,
// Defined in XPCShellImpl. // Defined in XPCShellImpl.
"sendCommand": false, sendCommand: false,
"strictEqual": false, strictEqual: false,
"throws": false, throws: false,
"todo": false, todo: false,
"todo_check_false": false, todo_check_false: false,
"todo_check_true": false, todo_check_true: false,
// Firefox specific function. // Firefox specific function.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval
"uneval": false, uneval: false,
}, },
"overrides": [{ overrides: [
// If it is a head file, we turn off global unused variable checks, as it {
// would require searching the other test files to know if they are used or not. // If it is a head file, we turn off global unused variable checks, as it
// This would be expensive and slow, and it isn't worth it for head files. // would require searching the other test files to know if they are used or not.
// We could get developers to declare as exported, but that doesn't seem worth it. // This would be expensive and slow, and it isn't worth it for head files.
"files": "head*.js", // We could get developers to declare as exported, but that doesn't seem worth it.
"rules": { files: "head*.js",
"no-unused-vars": ["error", { rules: {
"args": "none", "no-unused-vars": [
"vars": "local", "error",
}], {
args: "none",
vars: "local",
},
],
},
}, },
}], ],
rules: { rules: {
"mozilla/import-headjs-globals": "error", "mozilla/import-headjs-globals": "error",

View file

@ -38,8 +38,8 @@ const extraDefinitions = [
// Via Components.utils, defineModuleGetter, defineLazyModuleGetters or // Via Components.utils, defineModuleGetter, defineLazyModuleGetters or
// defineLazyScriptGetter (and map to // defineLazyScriptGetter (and map to
// single) variable. // single) variable.
{name: "XPCOMUtils", writable: false}, { name: "XPCOMUtils", writable: false },
{name: "Task", writable: false}, { name: "Task", writable: false },
]; ];
// Some files in global-scripts.inc need mapping to specific locations. // Some files in global-scripts.inc need mapping to specific locations.
@ -51,13 +51,12 @@ const MAPPINGS = {
"places-tree.js": "browser/components/places/content/places-tree.js", "places-tree.js": "browser/components/places/content/places-tree.js",
}; };
const globalScriptsRegExp = const globalScriptsRegExp = /^\s*Services.scriptloader.loadSubScript\(\"(.*?)\", this\);$/;
/^\s*Services.scriptloader.loadSubScript\(\"(.*?)\", this\);$/;
function getGlobalScriptIncludes(scriptPath) { function getGlobalScriptIncludes(scriptPath) {
let fileData; let fileData;
try { try {
fileData = fs.readFileSync(scriptPath, {encoding: "utf8"}); fileData = fs.readFileSync(scriptPath, { encoding: "utf8" });
} catch (ex) { } catch (ex) {
// The file isn't present, so this isn't an m-c repository. // The file isn't present, so this isn't an m-c repository.
return null; return null;
@ -71,9 +70,12 @@ function getGlobalScriptIncludes(scriptPath) {
let match = line.match(globalScriptsRegExp); let match = line.match(globalScriptsRegExp);
if (match) { if (match) {
let sourceFile = match[1] let sourceFile = match[1]
.replace("chrome://browser/content/search/", "browser/components/search/content/") .replace(
.replace("chrome://browser/content/", "browser/base/content/") "chrome://browser/content/search/",
.replace("chrome://global/content/", "toolkit/content/"); "browser/components/search/content/"
)
.replace("chrome://browser/content/", "browser/base/content/")
.replace("chrome://global/content/", "toolkit/content/");
for (let mapping of Object.getOwnPropertyNames(MAPPINGS)) { for (let mapping of Object.getOwnPropertyNames(MAPPINGS)) {
if (sourceFile.includes(mapping)) { if (sourceFile.includes(mapping)) {
@ -110,7 +112,8 @@ function getScriptGlobals() {
} catch (e) { } catch (e) {
console.error(`Could not load globals from file ${fileName}: ${e}`); console.error(`Could not load globals from file ${fileName}: ${e}`);
console.error( console.error(
`You may need to update the mappings in ${module.filename}`); `You may need to update the mappings in ${module.filename}`
);
throw new Error(`Could not load globals from file ${fileName}: ${e}`); throw new Error(`Could not load globals from file ${fileName}: ${e}`);
} }
} }
@ -133,6 +136,6 @@ function getMozillaCentralItems() {
}; };
} }
module.exports = helpers.isMozillaCentralBased() ? module.exports = helpers.isMozillaCentralBased()
getMozillaCentralItems() : ? getMozillaCentralItems()
helpers.getSavedEnvironmentItems("browser-window"); : helpers.getSavedEnvironmentItems("browser-window");

View file

@ -13,9 +13,12 @@
var globals = require("globals"); var globals = require("globals");
var util = require("util"); var util = require("util");
var workerGlobals = util._extend({ var workerGlobals = util._extend(
ctypes: false, {
}, globals.worker); ctypes: false,
},
globals.worker
);
module.exports = { module.exports = {
globals: workerGlobals, globals: workerGlobals,

View file

@ -9,16 +9,16 @@
"use strict"; "use strict";
module.exports = { module.exports = {
"globals": { globals: {
// These globals are hard-coded and available in .jsm scopes. // These globals are hard-coded and available in .jsm scopes.
// https://searchfox.org/mozilla-central/rev/ed212c79cfe86357e9a5740082b9364e7f6e526f/js/xpconnect/loader/mozJSComponentLoader.cpp#134-140 // https://searchfox.org/mozilla-central/rev/ed212c79cfe86357e9a5740082b9364e7f6e526f/js/xpconnect/loader/mozJSComponentLoader.cpp#134-140
"atob": false, atob: false,
"btoa": false, btoa: false,
"debug": false, debug: false,
"dump": false, dump: false,
// The WebAssembly global is available in most (if not all) contexts where // The WebAssembly global is available in most (if not all) contexts where
// JS can run. It's definitely available in JSMs. So even if this is not // JS can run. It's definitely available in JSMs. So even if this is not
// the perfect place to add it, it's not wrong, and we can move it later. // the perfect place to add it, it's not wrong, and we can move it later.
"WebAssembly": false, WebAssembly: false,
}, },
}; };

View file

@ -54,7 +54,7 @@ function mapGlobals(fileGlobals) {
} }
module.exports = { module.exports = {
globals: helpers.isMozillaCentralBased() ? globals: helpers.isMozillaCentralBased()
mapGlobals(getScriptGlobals()) : ? mapGlobals(getScriptGlobals())
helpers.getSavedEnvironmentItems("simpletest").globals, : helpers.getSavedEnvironmentItems("simpletest").globals,
}; };

View file

@ -45,7 +45,7 @@ function parseBooleanConfig(string, comment) {
} }
items[name] = { items[name] = {
value: (value === "true"), value: value === "true",
comment, comment,
}; };
}); });
@ -135,7 +135,10 @@ GlobalsForNode.prototype = {
// Note: We check the expression types here and only call the necessary // Note: We check the expression types here and only call the necessary
// functions to aid performance. // functions to aid performance.
if (node.expression.type === "AssignmentExpression") { if (node.expression.type === "AssignmentExpression") {
globals = helpers.convertThisAssignmentExpressionToGlobals(node, isGlobal); globals = helpers.convertThisAssignmentExpressionToGlobals(
node,
isGlobal
);
} else if (node.expression.type === "CallExpression") { } else if (node.expression.type === "CallExpression") {
globals = helpers.convertCallExpressionToGlobals(node, isGlobal); globals = helpers.convertCallExpressionToGlobals(node, isGlobal);
} }
@ -144,8 +147,11 @@ GlobalsForNode.prototype = {
// this is a worker. It would be nice if eslint gave us a way of getting // this is a worker. It would be nice if eslint gave us a way of getting
// the environment directly. // the environment directly.
if (globalScope && globalScope.set.get("importScripts")) { if (globalScope && globalScope.set.get("importScripts")) {
let workerDetails = helpers.convertWorkerExpressionToGlobals(node, let workerDetails = helpers.convertWorkerExpressionToGlobals(
isGlobal, this.dirname); node,
isGlobal,
this.dirname
);
globals = globals.concat(workerDetails); globals = globals.concat(workerDetails);
} }
@ -265,19 +271,31 @@ module.exports = {
} else if (script.src.includes("chrome")) { } else if (script.src.includes("chrome")) {
// This is one way of referencing test files. // This is one way of referencing test files.
script.src = script.src.replace("chrome://mochikit/content/", "/"); script.src = script.src.replace("chrome://mochikit/content/", "/");
scriptName = path.join(helpers.rootDir, "testing", "mochitest", script.src); scriptName = path.join(
helpers.rootDir,
"testing",
"mochitest",
script.src
);
} else if (script.src.includes("SimpleTest")) { } else if (script.src.includes("SimpleTest")) {
// This is another way of referencing test files... // This is another way of referencing test files...
scriptName = path.join(helpers.rootDir, "testing", "mochitest", script.src); scriptName = path.join(
helpers.rootDir,
"testing",
"mochitest",
script.src
);
} else { } else {
// Fallback to hoping this is a relative path. // Fallback to hoping this is a relative path.
scriptName = path.join(dir, script.src); scriptName = path.join(dir, script.src);
} }
if (scriptName && fs.existsSync(scriptName)) { if (scriptName && fs.existsSync(scriptName)) {
globals.push(...module.exports.getGlobalsForFile(scriptName, { globals.push(
ecmaVersion: helpers.getECMAVersion(), ...module.exports.getGlobalsForFile(scriptName, {
sourceType: script.type, ecmaVersion: helpers.getECMAVersion(),
})); sourceType: script.type,
})
);
} }
} }
@ -317,7 +335,11 @@ module.exports = {
helpers.addGlobals(extraHTMLGlobals, globalScope); helpers.addGlobals(extraHTMLGlobals, globalScope);
} }
let globals = handler[type](node, context.getAncestors(), globalScope); let globals = handler[type](node, context.getAncestors(), globalScope);
helpers.addGlobals(globals, globalScope, node.type !== "Program" && node); helpers.addGlobals(
globals,
globalScope,
node.type !== "Program" && node
);
}; };
} }

View file

@ -53,7 +53,13 @@ module.exports = {
get modulesGlobalData() { get modulesGlobalData() {
if (!gModules) { if (!gModules) {
if (this.isMozillaCentralBased()) { if (this.isMozillaCentralBased()) {
gModules = require(path.join(this.rootDir, "tools", "lint", "eslint", "modules.json")); gModules = require(path.join(
this.rootDir,
"tools",
"lint",
"eslint",
"modules.json"
));
} else { } else {
gModules = require("./modules.json"); gModules = require("./modules.json");
} }
@ -78,7 +84,7 @@ module.exports = {
getAST(sourceText, astOptions = {}) { getAST(sourceText, astOptions = {}) {
// Use a permissive config file to allow parsing of anything that Espree // Use a permissive config file to allow parsing of anything that Espree
// can parse. // can parse.
let config = {...this.getPermissiveConfig(), ...astOptions}; let config = { ...this.getPermissiveConfig(), ...astOptions };
return espree.parse(sourceText, config); return espree.parse(sourceText, config);
}, },
@ -97,10 +103,15 @@ module.exports = {
case "MemberExpression": case "MemberExpression":
if (node.computed) { if (node.computed) {
let filename = context && context.getFilename(); let filename = context && context.getFilename();
throw new Error(`getASTSource unsupported computed MemberExpression in ${filename}`); throw new Error(
`getASTSource unsupported computed MemberExpression in ${filename}`
);
} }
return this.getASTSource(node.object) + "." + return (
this.getASTSource(node.property); this.getASTSource(node.object) +
"." +
this.getASTSource(node.property)
);
case "ThisExpression": case "ThisExpression":
return "this"; return "this";
case "Identifier": case "Identifier":
@ -121,10 +132,17 @@ module.exports = {
case "ArrowFunctionExpression": case "ArrowFunctionExpression":
return "() => {}"; return "() => {}";
case "AssignmentExpression": case "AssignmentExpression":
return this.getASTSource(node.left) + " = " + return (
this.getASTSource(node.right); this.getASTSource(node.left) + " = " + this.getASTSource(node.right)
);
case "BinaryExpression": case "BinaryExpression":
return this.getASTSource(node.left) + " " + node.operator + " " + this.getASTSource(node.right); return (
this.getASTSource(node.left) +
" " +
node.operator +
" " +
this.getASTSource(node.right)
);
default: default:
throw new Error("getASTSource unsupported node type: " + node.type); throw new Error("getASTSource unsupported node type: " + node.type);
} }
@ -187,10 +205,12 @@ module.exports = {
let results = []; let results = [];
let expr = node.expression; let expr = node.expression;
if (node.expression.type === "CallExpression" && if (
expr.callee && node.expression.type === "CallExpression" &&
expr.callee.type === "Identifier" && expr.callee &&
expr.callee.name === "importScripts") { expr.callee.type === "Identifier" &&
expr.callee.name === "importScripts"
) {
for (var arg of expr.arguments) { for (var arg of expr.arguments) {
var match = arg.value && arg.value.match(workerImportFilenameMatch); var match = arg.value && arg.value.match(workerImportFilenameMatch);
if (match) { if (match) {
@ -201,9 +221,11 @@ module.exports = {
results = results.concat(additionalGlobals); results = results.concat(additionalGlobals);
} }
} else if (match[2] in globalModules) { } else if (match[2] in globalModules) {
results = results.concat(globalModules[match[2]].map(name => { results = results.concat(
return { name, writable: true }; globalModules[match[2]].map(name => {
})); return { name, writable: true };
})
);
} else { } else {
results.push({ name: match[3], writable: true, explicit: true }); results.push({ name: match[3], writable: true, explicit: true });
} }
@ -231,12 +253,14 @@ module.exports = {
* If the global is writeable or not. * If the global is writeable or not.
*/ */
convertThisAssignmentExpressionToGlobals(node, isGlobal) { convertThisAssignmentExpressionToGlobals(node, isGlobal) {
if (isGlobal && if (
node.expression.left && isGlobal &&
node.expression.left.object && node.expression.left &&
node.expression.left.object.type === "ThisExpression" && node.expression.left.object &&
node.expression.left.property && node.expression.left.object.type === "ThisExpression" &&
node.expression.left.property.type === "Identifier") { node.expression.left.property &&
node.expression.left.property.type === "Identifier"
) {
return [{ name: node.expression.left.property.name, writable: true }]; return [{ name: node.expression.left.property.name, writable: true }];
} }
return []; return [];
@ -260,14 +284,16 @@ module.exports = {
*/ */
convertCallExpressionToGlobals(node, isGlobal) { convertCallExpressionToGlobals(node, isGlobal) {
let express = node.expression; let express = node.expression;
if (express.type === "CallExpression" && if (
express.callee.type === "MemberExpression" && express.type === "CallExpression" &&
express.callee.object && express.callee.type === "MemberExpression" &&
express.callee.object.type === "Identifier" && express.callee.object &&
express.arguments.length === 1 && express.callee.object.type === "Identifier" &&
express.arguments[0].type === "ArrayExpression" && express.arguments.length === 1 &&
express.callee.property.type === "Identifier" && express.arguments[0].type === "ArrayExpression" &&
express.callee.property.name === "importGlobalProperties") { express.callee.property.type === "Identifier" &&
express.callee.property.name === "importGlobalProperties"
) {
return express.arguments[0].elements.map(literal => { return express.arguments[0].elements.map(literal => {
return { return {
explicit: true, explicit: true,
@ -301,7 +327,9 @@ module.exports = {
// of them. // of them.
let explicit = globalModules[match[1]].length == 1; let explicit = globalModules[match[1]].length == 1;
return globalModules[match[1]].map(name => ({ return globalModules[match[1]].map(name => ({
name, writable: true, explicit, name,
writable: true,
explicit,
})); }));
} }
@ -322,27 +350,43 @@ module.exports = {
} }
} }
if (callExpressionMultiDefinitions.some(expr => source.startsWith(expr)) && if (
node.expression.arguments[1]) { callExpressionMultiDefinitions.some(expr => source.startsWith(expr)) &&
node.expression.arguments[1]
) {
let arg = node.expression.arguments[1]; let arg = node.expression.arguments[1];
if (arg.type === "ObjectExpression") { if (arg.type === "ObjectExpression") {
return arg.properties return arg.properties
.map(p => ({ name: p.type === "Property" && p.key.name, writable: true, explicit: true })) .map(p => ({
.filter(g => g.name); name: p.type === "Property" && p.key.name,
writable: true,
explicit: true,
}))
.filter(g => g.name);
} }
if (arg.type === "ArrayExpression") { if (arg.type === "ArrayExpression") {
return arg.elements return arg.elements
.map(p => ({ name: p.type === "Literal" && p.value, writable: true, explicit: true })) .map(p => ({
.filter(g => typeof g.name == "string"); name: p.type === "Literal" && p.value,
writable: true,
explicit: true,
}))
.filter(g => typeof g.name == "string");
} }
} }
if (node.expression.callee.type == "MemberExpression" && if (
node.expression.callee.property.type == "Identifier" && node.expression.callee.type == "MemberExpression" &&
node.expression.callee.property.name == "defineLazyScriptGetter") { node.expression.callee.property.type == "Identifier" &&
node.expression.callee.property.name == "defineLazyScriptGetter"
) {
// The case where we have a single symbol as a string has already been // The case where we have a single symbol as a string has already been
// handled by the regexp, so we have an array of symbols here. // handled by the regexp, so we have an array of symbols here.
return node.expression.arguments[1].elements.map(n => ({ name: n.value, writable: true, explicit: true })); return node.expression.arguments[1].elements.map(n => ({
name: n.value,
writable: true,
explicit: true,
}));
} }
return []; return [];
@ -368,7 +412,7 @@ module.exports = {
variable.eslintExplicitGlobal = false; variable.eslintExplicitGlobal = false;
variable.writeable = writable; variable.writeable = writable;
if (node) { if (node) {
variable.defs.push({node, name: {name}}); variable.defs.push({ node, name: { name } });
variable.identifiers.push(node); variable.identifiers.push(node);
} }
@ -402,7 +446,9 @@ module.exports = {
* The AST node that defined the globals. * The AST node that defined the globals.
*/ */
addGlobals(globalVars, scope, node) { addGlobals(globalVars, scope, node) {
globalVars.forEach(v => this.addVarToScope(v.name, scope, v.writable, v.explicit && node)); globalVars.forEach(v =>
this.addVarToScope(v.name, scope, v.writable, v.explicit && node)
);
}, },
/** /**
@ -503,11 +549,14 @@ module.exports = {
let filepath = this.cleanUpPath(scope.getFilename()); let filepath = this.cleanUpPath(scope.getFilename());
let dir = path.dirname(filepath); let dir = path.dirname(filepath);
let names = let names = fs
fs.readdirSync(dir) .readdirSync(dir)
.filter(name => (name.startsWith("head") || .filter(
name.startsWith("xpcshell-head")) && name.endsWith(".js")) name =>
.map(name => path.join(dir, name)); (name.startsWith("head") || name.startsWith("xpcshell-head")) &&
name.endsWith(".js")
)
.map(name => path.join(dir, name));
return names; return names;
}, },
@ -548,8 +597,7 @@ module.exports = {
file: path.join(dir, name), file: path.join(dir, name),
manifest, manifest,
}); });
} catch (e) { } catch (e) {}
}
} }
directoryManifests.set(dir, manifests); directoryManifests.set(dir, manifests);
@ -673,7 +721,10 @@ module.exports = {
return null; return null;
} }
let possibleRoot = searchUpForIgnore(path.dirname(module.filename), ".eslintignore"); let possibleRoot = searchUpForIgnore(
path.dirname(module.filename),
".eslintignore"
);
if (!possibleRoot) { if (!possibleRoot) {
possibleRoot = searchUpForIgnore(path.resolve(), ".eslintignore"); possibleRoot = searchUpForIgnore(path.resolve(), ".eslintignore");
} }
@ -717,9 +768,9 @@ module.exports = {
// without any path info (happens in Atom with linter-eslint) // without any path info (happens in Atom with linter-eslint)
return path.join(cwd, fileName); return path.join(cwd, fileName);
} }
// Case 1: executed form in a nested directory, e.g. from a text editor: // Case 1: executed form in a nested directory, e.g. from a text editor:
// fileName: a/b/c/d.js // fileName: a/b/c/d.js
// cwd: /path/to/mozilla/repo/a/b/c // cwd: /path/to/mozilla/repo/a/b/c
var dirName = path.dirname(fileName); var dirName = path.dirname(fileName);
return cwd.slice(0, cwd.length - dirName.length) + fileName; return cwd.slice(0, cwd.length - dirName.length) + fileName;
}, },
@ -736,7 +787,13 @@ module.exports = {
get globalScriptPaths() { get globalScriptPaths() {
return [ return [
path.join(this.rootDir, "browser", "base", "content", "browser.xhtml"), path.join(this.rootDir, "browser", "base", "content", "browser.xhtml"),
path.join(this.rootDir, "browser", "base", "content", "global-scripts.inc"), path.join(
this.rootDir,
"browser",
"base",
"content",
"global-scripts.inc"
),
]; ];
}, },
@ -745,7 +802,9 @@ module.exports = {
}, },
getSavedEnvironmentItems(environment) { getSavedEnvironmentItems(environment) {
return require("./environments/saved-globals.json").environments[environment]; return require("./environments/saved-globals.json").environments[
environment
];
}, },
getSavedRuleData(rule) { getSavedRuleData(rule) {

View file

@ -16,16 +16,16 @@ module.exports = {
"browser-test": require("../lib/configs/browser-test"), "browser-test": require("../lib/configs/browser-test"),
"chrome-test": require("../lib/configs/chrome-test"), "chrome-test": require("../lib/configs/chrome-test"),
"mochitest-test": require("../lib/configs/mochitest-test"), "mochitest-test": require("../lib/configs/mochitest-test"),
"recommended": require("../lib/configs/recommended"), recommended: require("../lib/configs/recommended"),
"xpcshell-test": require("../lib/configs/xpcshell-test"), "xpcshell-test": require("../lib/configs/xpcshell-test"),
}, },
environments: { environments: {
"browser-window": require("../lib/environments/browser-window.js"), "browser-window": require("../lib/environments/browser-window.js"),
"chrome-worker": require("../lib/environments/chrome-worker.js"), "chrome-worker": require("../lib/environments/chrome-worker.js"),
"frame-script": require("../lib/environments/frame-script.js"), "frame-script": require("../lib/environments/frame-script.js"),
"jsm": require("../lib/environments/jsm.js"), jsm: require("../lib/environments/jsm.js"),
"simpletest": require("../lib/environments/simpletest.js"), simpletest: require("../lib/environments/simpletest.js"),
"privileged": require("../lib/environments/privileged.js"), privileged: require("../lib/environments/privileged.js"),
}, },
processors: { processors: {
".xml": require("../lib/processors/xbl-bindings"), ".xml": require("../lib/processors/xbl-bindings"),
@ -36,10 +36,8 @@ module.exports = {
"avoid-removeChild": require("../lib/rules/avoid-removeChild"), "avoid-removeChild": require("../lib/rules/avoid-removeChild"),
"balanced-listeners": require("../lib/rules/balanced-listeners"), "balanced-listeners": require("../lib/rules/balanced-listeners"),
"consistent-if-bracing": require("../lib/rules/consistent-if-bracing"), "consistent-if-bracing": require("../lib/rules/consistent-if-bracing"),
"import-browser-window-globals": "import-browser-window-globals": require("../lib/rules/import-browser-window-globals"),
require("../lib/rules/import-browser-window-globals"), "import-content-task-globals": require("../lib/rules/import-content-task-globals"),
"import-content-task-globals":
require("../lib/rules/import-content-task-globals"),
"import-globals": require("../lib/rules/import-globals"), "import-globals": require("../lib/rules/import-globals"),
"import-headjs-globals": require("../lib/rules/import-headjs-globals"), "import-headjs-globals": require("../lib/rules/import-headjs-globals"),
"mark-exported-symbols-as-used": require("../lib/rules/mark-exported-symbols-as-used"), "mark-exported-symbols-as-used": require("../lib/rules/mark-exported-symbols-as-used"),
@ -50,19 +48,15 @@ module.exports = {
"no-define-cc-etc": require("../lib/rules/no-define-cc-etc"), "no-define-cc-etc": require("../lib/rules/no-define-cc-etc"),
"no-task": require("../lib/rules/no-task"), "no-task": require("../lib/rules/no-task"),
"no-useless-parameters": require("../lib/rules/no-useless-parameters"), "no-useless-parameters": require("../lib/rules/no-useless-parameters"),
"no-useless-removeEventListener": "no-useless-removeEventListener": require("../lib/rules/no-useless-removeEventListener"),
require("../lib/rules/no-useless-removeEventListener"), "no-useless-run-test": require("../lib/rules/no-useless-run-test"),
"no-useless-run-test": "reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"),
require("../lib/rules/no-useless-run-test"),
"reject-importGlobalProperties":
require("../lib/rules/reject-importGlobalProperties"),
"reject-some-requires": require("../lib/rules/reject-some-requires"), "reject-some-requires": require("../lib/rules/reject-some-requires"),
"rejects-requires-await": require("../lib/rules/rejects-requires-await"), "rejects-requires-await": require("../lib/rules/rejects-requires-await"),
"use-cc-etc": require("../lib/rules/use-cc-etc"), "use-cc-etc": require("../lib/rules/use-cc-etc"),
"use-chromeutils-generateqi": require("../lib/rules/use-chromeutils-generateqi"), "use-chromeutils-generateqi": require("../lib/rules/use-chromeutils-generateqi"),
"use-chromeutils-import": require("../lib/rules/use-chromeutils-import"), "use-chromeutils-import": require("../lib/rules/use-chromeutils-import"),
"use-default-preference-values": "use-default-preference-values": require("../lib/rules/use-default-preference-values"),
require("../lib/rules/use-default-preference-values"),
"use-ownerGlobal": require("../lib/rules/use-ownerGlobal"), "use-ownerGlobal": require("../lib/rules/use-ownerGlobal"),
"use-includes-instead-of-indexOf": require("../lib/rules/use-includes-instead-of-indexOf"), "use-includes-instead-of-indexOf": require("../lib/rules/use-includes-instead-of-indexOf"),
"use-returnValue": require("../lib/rules/use-returnValue"), "use-returnValue": require("../lib/rules/use-returnValue"),

View file

@ -61,8 +61,11 @@ function addNodeLines(node, reindent) {
// If the second to last line is also blank and has a higher indent than the // If the second to last line is also blank and has a higher indent than the
// last one, then the CDATA block doesn't close with the closing tag. // last one, then the CDATA block doesn't close with the closing tag.
if (lines.length > 2 && lines[lines.length - 2].trim() == "" && if (
lines[lines.length - 2].length > lastIndent) { lines.length > 2 &&
lines[lines.length - 2].trim() == "" &&
lines[lines.length - 2].length > lastIndent
) {
lastIndent = lines[lines.length - 2].length; lastIndent = lines[lines.length - 2].length;
} }
@ -82,8 +85,9 @@ function addNodeLines(node, reindent) {
// Find the preceding whitespace for all lines that aren't entirely // Find the preceding whitespace for all lines that aren't entirely
// whitespace. // whitespace.
let indents = lines.filter(s => s.trim().length > 0) let indents = lines
.map(s => s.length - s.trimLeft().length); .filter(s => s.trim().length > 0)
.map(s => s.length - s.trimLeft().length);
// Find the smallest indent level in use // Find the smallest indent level in use
let minIndent = Math.min.apply(null, indents); let minIndent = Math.min.apply(null, indents);
let indent = Math.max(2, minIndent - lastIndent); let indent = Math.max(2, minIndent - lastIndent);
@ -172,7 +176,10 @@ module.exports = {
continue; continue;
} }
addSyntheticLine(`get ${item.attributes.name}() {`, item.textLine); addSyntheticLine(
`get ${item.attributes.name}() {`,
item.textLine
);
addSyntheticLine(`return (`, item.textLine); addSyntheticLine(`return (`, item.textLine);
// Remove trailing semicolons, as we are adding our own // Remove trailing semicolons, as we are adding our own
@ -195,16 +202,20 @@ module.exports = {
// Methods become function declarations with the appropriate // Methods become function declarations with the appropriate
// params. // params.
let params = item.children.filter(n => { let params = item.children
return n.local == "parameter" && n.namespace == NS_XBL; .filter(n => {
}) return n.local == "parameter" && n.namespace == NS_XBL;
.map(n => n.attributes.name) })
.join(", "); .map(n => n.attributes.name)
.join(", ");
let body = item.children.filter(n => { let body = item.children.filter(n => {
return n.local == "body" && n.namespace == NS_XBL; return n.local == "body" && n.namespace == NS_XBL;
})[0]; })[0];
addSyntheticLine(`${item.attributes.name}(${params}) {`, item.textLine); addSyntheticLine(
`${item.attributes.name}(${params}) {`,
item.textLine
);
addNodeLines(body, 4); addNodeLines(body, 4);
addSyntheticLine(`},`, item.textEndLine); addSyntheticLine(`},`, item.textEndLine);
break; break;
@ -217,9 +228,15 @@ module.exports = {
} }
if (propdef.local == "setter") { if (propdef.local == "setter") {
addSyntheticLine(`set ${item.attributes.name}(val) {`, propdef.textLine); addSyntheticLine(
`set ${item.attributes.name}(val) {`,
propdef.textLine
);
} else if (propdef.local == "getter") { } else if (propdef.local == "getter") {
addSyntheticLine(`get ${item.attributes.name}() {`, propdef.textLine); addSyntheticLine(
`get ${item.attributes.name}() {`,
propdef.textLine
);
} else { } else {
continue; continue;
} }
@ -241,7 +258,10 @@ module.exports = {
} }
} }
addSyntheticLine((part.local == "implementation" ? `},` : `],`), part.textEndLine); addSyntheticLine(
part.local == "implementation" ? `},` : `],`,
part.textEndLine
);
} }
addSyntheticLine(`},`, binding.textEndLine); addSyntheticLine(`},`, binding.textEndLine);
} }

View file

@ -51,8 +51,12 @@ function dealWithIfdefs(text, filename) {
if (!line.startsWith("#")) { if (!line.startsWith("#")) {
outputLines.push(shouldSkip ? "" : line); outputLines.push(shouldSkip ? "" : line);
} else { } else {
if (line.startsWith("# ") || line.startsWith("#filter") || if (
line == "#" || line.startsWith("#define")) { line.startsWith("# ") ||
line.startsWith("#filter") ||
line == "#" ||
line.startsWith("#define")
) {
outputLines.push(""); outputLines.push("");
continue; continue;
} }
@ -86,7 +90,7 @@ function dealWithIfdefs(text, filename) {
if (!shouldSkip) { if (!shouldSkip) {
let fileToInclude = line.substr("#include ".length).trim(); let fileToInclude = line.substr("#include ".length).trim();
let subpath = path.join(path.dirname(innerFile), fileToInclude); let subpath = path.join(path.dirname(innerFile), fileToInclude);
let contents = fs.readFileSync(subpath, {encoding: "utf-8"}); let contents = fs.readFileSync(subpath, { encoding: "utf-8" });
contents = contents.split(/\n/); contents = contents.split(/\n/);
// Recurse: // Recurse:
contents = stripIfdefsFromLines(contents, subpath); contents = stripIfdefsFromLines(contents, subpath);
@ -188,7 +192,7 @@ module.exports = {
"/* eslint-disable quotes */", "/* eslint-disable quotes */",
"/* eslint-disable no-undef */", "/* eslint-disable no-undef */",
]; ];
lineMap = scriptLines.map(() => ({line: 0})); lineMap = scriptLines.map(() => ({ line: 0 }));
includedRanges = []; includedRanges = [];
// Do C-style preprocessing first: // Do C-style preprocessing first:
text = dealWithIfdefs(text, filename); text = dealWithIfdefs(text, filename);
@ -225,17 +229,22 @@ module.exports = {
let mapped = lineMap[message.line - 1]; let mapped = lineMap[message.line - 1];
// Ensure we don't modify this by making a copy. We might need it for another failure. // Ensure we don't modify this by making a copy. We might need it for another failure.
let target = mapped.line; let target = mapped.line;
let includedRange = includedRanges.find(r => (target >= r.start && target <= r.end)); let includedRange = includedRanges.find(
r => target >= r.start && target <= r.end
);
// If this came from an #included file, indicate this in the message // If this came from an #included file, indicate this in the message
if (includedRange) { if (includedRange) {
target = includedRange.start; target = includedRange.start;
message.message += " (from included file " + path.basename(includedRange.filename) + ")"; message.message +=
" (from included file " +
path.basename(includedRange.filename) +
")";
} }
// Compensate for line numbers shifting as a result of #include: // Compensate for line numbers shifting as a result of #include:
let includeBallooning = let includeBallooning = includedRanges
includedRanges.filter(r => (target >= r.end)) .filter(r => target >= r.end)
.map(r => r.end - r.start) .map(r => r.end - r.start)
.reduce((acc, next) => acc + next, 0); .reduce((acc, next) => acc + next, 0);
target -= includeBallooning; target -= includeBallooning;
// Add back the 1 to go back to 1-indexing. // Add back the 1 to go back to 1-indexing.
message.line = target + 1; message.line = target + 1;
@ -250,4 +259,3 @@ module.exports = {
return errors; return errors;
}, },
}; };

View file

@ -27,32 +27,41 @@ module.exports = {
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type !== "MemberExpression" || if (
callee.object.type !== "Identifier" || callee.type !== "MemberExpression" ||
callee.object.name !== "Date" || callee.object.type !== "Identifier" ||
callee.property.type !== "Identifier" || callee.object.name !== "Date" ||
callee.property.name !== "now") { callee.property.type !== "Identifier" ||
callee.property.name !== "now"
) {
return; return;
} }
context.report(node, "use performance.now() instead of Date.now() for timing " + context.report(
"measurements"); node,
"use performance.now() instead of Date.now() for timing " +
"measurements"
);
}, },
"NewExpression": function(node) { NewExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type !== "Identifier" || if (
callee.name !== "Date" || callee.type !== "Identifier" ||
node.arguments.length > 0) { callee.name !== "Date" ||
node.arguments.length > 0
) {
return; return;
} }
context.report(node, "use performance.now() instead of new Date() for timing " + context.report(
"measurements"); node,
"use performance.now() instead of new Date() for timing " +
"measurements"
);
}, },
}; };
}, },
}; };

View file

@ -21,31 +21,43 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"CallExpression": function(node) { CallExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type !== "MemberExpression" || if (
callee.property.type !== "Identifier" || callee.type !== "MemberExpression" ||
callee.property.name != "removeChild" || callee.property.type !== "Identifier" ||
node.arguments.length != 1) { callee.property.name != "removeChild" ||
node.arguments.length != 1
) {
return; return;
} }
if (callee.object.type == "MemberExpression" && if (
callee.object.property.type == "Identifier" && callee.object.type == "MemberExpression" &&
callee.object.property.name == "parentNode" && callee.object.property.type == "Identifier" &&
helpers.getASTSource(callee.object.object, context) == callee.object.property.name == "parentNode" &&
helpers.getASTSource(node.arguments[0])) { helpers.getASTSource(callee.object.object, context) ==
context.report(node, "use element.remove() instead of " + helpers.getASTSource(node.arguments[0])
"element.parentNode.removeChild(element)"); ) {
context.report(
node,
"use element.remove() instead of " +
"element.parentNode.removeChild(element)"
);
} }
if (node.arguments[0].type == "MemberExpression" && if (
node.arguments[0].property.type == "Identifier" && node.arguments[0].type == "MemberExpression" &&
node.arguments[0].property.name == "firstChild" && node.arguments[0].property.type == "Identifier" &&
helpers.getASTSource(callee.object, context) == node.arguments[0].property.name == "firstChild" &&
helpers.getASTSource(node.arguments[0].object)) { helpers.getASTSource(callee.object, context) ==
context.report(node, "use element.firstChild.remove() instead of " + helpers.getASTSource(node.arguments[0].object)
"element.removeChild(element.firstChild)"); ) {
context.report(
node,
"use element.firstChild.remove() instead of " +
"element.removeChild(element.firstChild)"
);
} }
}, },
}; };

View file

@ -22,8 +22,8 @@ module.exports = function(context) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
var DICTIONARY = { var DICTIONARY = {
"addEventListener": "removeEventListener", addEventListener: "removeEventListener",
"on": "off", on: "off",
}; };
// Invert this dictionary to make it easy later. // Invert this dictionary to make it easy later.
var INVERTED_DICTIONARY = {}; var INVERTED_DICTIONARY = {};
@ -40,13 +40,17 @@ module.exports = function(context) {
let options = node.arguments[2]; let options = node.arguments[2];
if (options) { if (options) {
if (options.type == "ObjectExpression") { if (options.type == "ObjectExpression") {
if (options.properties.some(p => p.key.name == "once" && if (
p.value.value === true)) { options.properties.some(
p => p.key.name == "once" && p.value.value === true
)
) {
// No point in adding listeners using the 'once' option. // No point in adding listeners using the 'once' option.
return; return;
} }
capture = options.properties.some(p => p.key.name == "capture" && capture = options.properties.some(
p.value.value === true); p => p.key.name == "capture" && p.value.value === true
);
} else { } else {
capture = options.value; capture = options.value;
} }
@ -64,8 +68,9 @@ module.exports = function(context) {
let options = node.arguments[2]; let options = node.arguments[2];
if (options) { if (options) {
if (options.type == "ObjectExpression") { if (options.type == "ObjectExpression") {
capture = options.properties.some(p => p.key.name == "capture" && capture = options.properties.some(
p.value.value === true); p => p.key.name == "capture" && p.value.value === true
);
} else { } else {
capture = options.value; capture = options.value;
} }
@ -93,9 +98,11 @@ module.exports = function(context) {
function hasRemovedListener(addedListener) { function hasRemovedListener(addedListener) {
for (var k = 0; k < removedListeners.length; k++) { for (var k = 0; k < removedListeners.length; k++) {
var listener = removedListeners[k]; var listener = removedListeners[k];
if (DICTIONARY[addedListener.functionName] === listener.functionName && if (
addedListener.type === listener.type && DICTIONARY[addedListener.functionName] === listener.functionName &&
addedListener.useCapture === listener.useCapture) { addedListener.type === listener.type &&
addedListener.useCapture === listener.useCapture
) {
return true; return true;
} }
} }
@ -126,12 +133,14 @@ module.exports = function(context) {
"Program:exit": function() { "Program:exit": function() {
getUnbalancedListeners().forEach(function(listener) { getUnbalancedListeners().forEach(function(listener) {
context.report(listener.node, context.report(
listener.node,
"No corresponding '{{functionName}}({{type}})' was found.", "No corresponding '{{functionName}}({{type}})' was found.",
{ {
functionName: DICTIONARY[listener.functionName], functionName: DICTIONARY[listener.functionName],
type: listener.type, type: listener.type,
}); }
);
}); });
}, },
}; };

View file

@ -20,10 +20,17 @@ module.exports = {
IfStatement(node) { IfStatement(node) {
if (node.parent.type !== "IfStatement") { if (node.parent.type !== "IfStatement") {
let types = new Set(); let types = new Set();
for (let currentNode = node; currentNode; currentNode = currentNode.alternate) { for (
let currentNode = node;
currentNode;
currentNode = currentNode.alternate
) {
let type = currentNode.consequent.type; let type = currentNode.consequent.type;
types.add(type == "BlockStatement" ? "Block" : "NotBlock"); types.add(type == "BlockStatement" ? "Block" : "NotBlock");
if (currentNode.alternate && currentNode.alternate.type !== "IfStatement") { if (
currentNode.alternate &&
currentNode.alternate.type !== "IfStatement"
) {
type = currentNode.alternate.type; type = currentNode.alternate.type;
types.add(type == "BlockStatement" ? "Block" : "NotBlock"); types.add(type == "BlockStatement" ? "Block" : "NotBlock");
break; break;

View file

@ -32,11 +32,16 @@ module.exports = function(context) {
relativePath = relativePath.split(path.sep).join("/"); relativePath = relativePath.split(path.sep).join("/");
} }
if (browserWindowEnv.browserjsScripts && if (
browserWindowEnv.browserjsScripts.includes(relativePath)) { browserWindowEnv.browserjsScripts &&
browserWindowEnv.browserjsScripts.includes(relativePath)
) {
for (let global in browserWindowEnv.globals) { for (let global in browserWindowEnv.globals) {
helpers.addVarToScope(global, context.getScope(), helpers.addVarToScope(
browserWindowEnv.globals[global]); global,
context.getScope(),
browserWindowEnv.globals[global]
);
} }
} }
}, },

View file

@ -25,10 +25,15 @@ module.exports = function(context) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
return { return {
"CallExpression[callee.object.name='ContentTask'][callee.property.name='spawn']": function(node) { "CallExpression[callee.object.name='ContentTask'][callee.property.name='spawn']": function(
node
) {
for (let global in frameScriptEnv.globals) { for (let global in frameScriptEnv.globals) {
helpers.addVarToScope(global, context.getScope(), helpers.addVarToScope(
frameScriptEnv.globals[global]); global,
context.getScope(),
frameScriptEnv.globals[global]
);
} }
}, },
}; };

View file

@ -43,11 +43,13 @@ module.exports = function(context) {
return { return {
AssignmentExpression(node, parents) { AssignmentExpression(node, parents) {
if (node.operator === "=" && if (
node.left.type === "MemberExpression" && node.operator === "=" &&
node.left.object.type === "ThisExpression" && node.left.type === "MemberExpression" &&
node.left.property.name === "EXPORTED_SYMBOLS" && node.left.object.type === "ThisExpression" &&
isGlobalScope()) { node.left.property.name === "EXPORTED_SYMBOLS" &&
isGlobalScope()
) {
markArrayElementsAsUsed(context, node, node.right); markArrayElementsAsUsed(context, node, node.right);
} }
}, },
@ -58,15 +60,18 @@ module.exports = function(context) {
} }
for (let item of node.declarations) { for (let item of node.declarations) {
if (item.id && if (
item.id.type == "Identifier" && item.id &&
item.id.name === "EXPORTED_SYMBOLS") { item.id.type == "Identifier" &&
item.id.name === "EXPORTED_SYMBOLS"
) {
if (node.kind === "let") { if (node.kind === "let") {
// The use of 'let' isn't allowed as the lexical scope may die after // The use of 'let' isn't allowed as the lexical scope may die after
// the script executes. // the script executes.
context.report({ context.report({
node, node,
message: "EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`", message:
"EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`",
}); });
} }

View file

@ -23,8 +23,7 @@ module.exports = function(context) {
} }
function deHungarianize(name) { function deHungarianize(name) {
return name.substring(1, 2).toLowerCase() + return name.substring(1, 2).toLowerCase() + name.substring(2, name.length);
name.substring(2, name.length);
} }
function checkFunction(node) { function checkFunction(node) {
@ -35,10 +34,12 @@ module.exports = function(context) {
name: param.name, name: param.name,
suggestion: deHungarianize(param.name), suggestion: deHungarianize(param.name),
}; };
context.report(param, context.report(
"Parameter '{{name}}' uses Hungarian Notation, " + param,
"consider using '{{suggestion}}' instead.", "Parameter '{{name}}' uses Hungarian Notation, " +
errorObj); "consider using '{{suggestion}}' instead.",
errorObj
);
} }
} }
} }
@ -48,8 +49,8 @@ module.exports = function(context) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
return { return {
"FunctionDeclaration": checkFunction, FunctionDeclaration: checkFunction,
"ArrowFunctionExpression": checkFunction, ArrowFunctionExpression: checkFunction,
"FunctionExpression": checkFunction, FunctionExpression: checkFunction,
}; };
}; };

View file

@ -13,10 +13,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
var helpers = require("../helpers"); var helpers = require("../helpers");
var testTypes = new Set([ var testTypes = new Set(["browser", "xpcshell"]);
"browser",
"xpcshell",
]);
module.exports = { module.exports = {
meta: { meta: {
@ -33,7 +30,7 @@ module.exports = {
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
// We don't want to run this on mochitest plain as it already // We don't want to run this on mochitest plain as it already
// prevents flaky setTimeout at runtime. This check is built-in // prevents flaky setTimeout at runtime. This check is built-in
// to the rule itself as sometimes other tests can live alongside // to the rule itself as sometimes other tests can live alongside
@ -44,14 +41,15 @@ module.exports = {
let callee = node.callee; let callee = node.callee;
if (callee.type === "MemberExpression") { if (callee.type === "MemberExpression") {
if (callee.property.name !== "setTimeout" || if (
callee.object.name !== "window" || callee.property.name !== "setTimeout" ||
node.arguments.length < 2) { callee.object.name !== "window" ||
node.arguments.length < 2
) {
return; return;
} }
} else if (callee.type === "Identifier") { } else if (callee.type === "Identifier") {
if (callee.name !== "setTimeout" || if (callee.name !== "setTimeout" || node.arguments.length < 2) {
node.arguments.length < 2) {
return; return;
} }
} else { } else {
@ -60,8 +58,11 @@ module.exports = {
let timeout = node.arguments[1]; let timeout = node.arguments[1];
if (timeout.type !== "Literal" || timeout.value > 0) { if (timeout.type !== "Literal" || timeout.value > 0) {
context.report(node, "listen for events instead of setTimeout() " + context.report(
"with arbitrary delay"); node,
"listen for events instead of setTimeout() " +
"with arbitrary delay"
);
} }
}, },
}; };

View file

@ -18,11 +18,16 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"BinaryExpression": function(node) { BinaryExpression(node) {
if (["==", "!="].includes(node.operator) && if (
(["true", "false"].includes(node.left.raw) || ["==", "!="].includes(node.operator) &&
["true", "false"].includes(node.right.raw))) { (["true", "false"].includes(node.left.raw) ||
context.report(node, "Don't compare for inexact equality against boolean literals"); ["true", "false"].includes(node.right.raw))
) {
context.report(
node,
"Don't compare for inexact equality against boolean literals"
);
} }
}, },
}; };

View file

@ -20,17 +20,31 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"VariableDeclarator": function(node) { VariableDeclarator(node) {
if (node.id.type == "Identifier" && componentsBlacklist.includes(node.id.name)) { if (
context.report(node, node.id.type == "Identifier" &&
`${node.id.name} is now defined in global scope, a separate definition is no longer necessary.`); componentsBlacklist.includes(node.id.name)
) {
context.report(
node,
`${
node.id.name
} is now defined in global scope, a separate definition is no longer necessary.`
);
} }
if (node.id.type == "ObjectPattern") { if (node.id.type == "ObjectPattern") {
for (let property of node.id.properties) { for (let property of node.id.properties) {
if (property.type == "Property" && componentsBlacklist.includes(property.value.name)) { if (
context.report(node, property.type == "Property" &&
`${property.value.name} is now defined in global scope, a separate definition is no longer necessary.`); componentsBlacklist.includes(property.value.name)
) {
context.report(
node,
`${
property.value.name
} is now defined in global scope, a separate definition is no longer necessary.`
);
} }
} }
} }

View file

@ -19,12 +19,14 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"CallExpression": function(node) { CallExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type === "MemberExpression" && if (
callee.object.type === "Identifier" && callee.type === "MemberExpression" &&
callee.object.name === "Task") { callee.object.type === "Identifier" &&
context.report({node, message: "Task.jsm is deprecated."}); callee.object.name === "Task"
) {
context.report({ node, message: "Task.jsm is deprecated." });
} }
}, },
}; };

View file

@ -16,8 +16,10 @@
module.exports = function(context) { module.exports = function(context) {
function getRangeAfterArgToEnd(argNumber, args) { function getRangeAfterArgToEnd(argNumber, args) {
let sourceCode = context.getSourceCode(); let sourceCode = context.getSourceCode();
return [sourceCode.getTokenAfter(args[argNumber]).range[0], return [
args[args.length - 1].range[1]]; sourceCode.getTokenAfter(args[argNumber]).range[0],
args[args.length - 1].range[1],
];
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -25,22 +27,29 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"CallExpression": function(node) { CallExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type !== "MemberExpression" || if (
callee.property.type !== "Identifier") { callee.type !== "MemberExpression" ||
callee.property.type !== "Identifier"
) {
return; return;
} }
let isFalse = arg => arg.type === "Literal" && arg.value === false; let isFalse = arg => arg.type === "Literal" && arg.value === false;
let isFalsy = arg => arg.type === "Literal" && !arg.value; let isFalsy = arg => arg.type === "Literal" && !arg.value;
let isBool = arg => arg.type === "Literal" && (arg.value === false || let isBool = arg =>
arg.value === true); arg.type === "Literal" && (arg.value === false || arg.value === true);
let name = callee.property.name; let name = callee.property.name;
let args = node.arguments; let args = node.arguments;
if (["addEventListener", "removeEventListener", "addObserver"] if (
.includes(name) && args.length === 3 && isFalse(args[2])) { ["addEventListener", "removeEventListener", "addObserver"].includes(
name
) &&
args.length === 3 &&
isFalse(args[2])
) {
context.report({ context.report({
node, node,
fix: fixer => { fix: fixer => {
@ -80,8 +89,7 @@ module.exports = function(context) {
}); });
} }
if (name === "notifyObservers" && args.length === 3 && if (name === "notifyObservers" && args.length === 3 && isFalsy(args[2])) {
isFalsy(args[2])) {
context.report({ context.report({
node, node,
fix: fixer => { fix: fixer => {
@ -91,8 +99,11 @@ module.exports = function(context) {
}); });
} }
if (name === "getComputedStyle" && args.length === 2 && if (
isFalsy(args[1])) { name === "getComputedStyle" &&
args.length === 2 &&
isFalsy(args[1])
) {
context.report({ context.report({
node, node,
fix: fixer => { fix: fixer => {
@ -102,8 +113,11 @@ module.exports = function(context) {
}); });
} }
if (name === "newURI" && args.length > 1 && if (
isFalsy(args[args.length - 1])) { name === "newURI" &&
args.length > 1 &&
isFalsy(args[args.length - 1])
) {
context.report({ context.report({
node, node,
fix: fixer => { fix: fixer => {
@ -111,7 +125,9 @@ module.exports = function(context) {
return fixer.removeRange(getRangeAfterArgToEnd(0, args)); return fixer.removeRange(getRangeAfterArgToEnd(0, args));
} }
return fixer.removeRange(getRangeAfterArgToEnd(args.length - 2, args)); return fixer.removeRange(
getRangeAfterArgToEnd(args.length - 2, args)
);
}, },
message: "newURI's last parameters are optional.", message: "newURI's last parameters are optional.",
}); });

View file

@ -19,37 +19,45 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"CallExpression": function(node) { CallExpression(node) {
let callee = node.callee; let callee = node.callee;
if (callee.type !== "MemberExpression" || if (
callee.property.type !== "Identifier" || callee.type !== "MemberExpression" ||
callee.property.name !== "addEventListener" || callee.property.type !== "Identifier" ||
node.arguments.length == 4) { callee.property.name !== "addEventListener" ||
node.arguments.length == 4
) {
return; return;
} }
let listener = node.arguments[1]; let listener = node.arguments[1];
if (!listener || if (
listener.type != "FunctionExpression" || !listener ||
!listener.body || listener.type != "FunctionExpression" ||
listener.body.type != "BlockStatement" || !listener.body ||
!listener.body.body.length || listener.body.type != "BlockStatement" ||
listener.body.body[0].type != "ExpressionStatement" || !listener.body.body.length ||
listener.body.body[0].expression.type != "CallExpression") { listener.body.body[0].type != "ExpressionStatement" ||
listener.body.body[0].expression.type != "CallExpression"
) {
return; return;
} }
let call = listener.body.body[0].expression; let call = listener.body.body[0].expression;
if (call.callee.type == "MemberExpression" && if (
call.callee.property.type == "Identifier" && call.callee.type == "MemberExpression" &&
call.callee.property.name == "removeEventListener" && call.callee.property.type == "Identifier" &&
((call.arguments[0].type == "Literal" && call.callee.property.name == "removeEventListener" &&
call.arguments[0].value == node.arguments[0].value) || ((call.arguments[0].type == "Literal" &&
(call.arguments[0].type == "Identifier" && call.arguments[0].value == node.arguments[0].value) ||
call.arguments[0].name == node.arguments[0].name))) { (call.arguments[0].type == "Identifier" &&
context.report(call, call.arguments[0].name == node.arguments[0].name))
"use {once: true} instead of removeEventListener as " + ) {
"the first instruction of the listener"); context.report(
call,
"use {once: true} instead of removeEventListener as " +
"the first instruction of the listener"
);
} }
}, },
}; };

View file

@ -19,12 +19,14 @@ module.exports = function(context) {
return { return {
"Program > FunctionDeclaration": function(node) { "Program > FunctionDeclaration": function(node) {
if (node.id.name === "run_test" && if (
node.body.type === "BlockStatement" && node.id.name === "run_test" &&
node.body.body.length === 1 && node.body.type === "BlockStatement" &&
node.body.body[0].type === "ExpressionStatement" && node.body.body.length === 1 &&
node.body.body[0].expression.type === "CallExpression" && node.body.body[0].type === "ExpressionStatement" &&
node.body.body[0].expression.callee.name === "run_next_test") { node.body.body[0].expression.type === "CallExpression" &&
node.body.body[0].expression.callee.name === "run_next_test"
) {
context.report({ context.report({
node, node,
fix: fixer => { fix: fixer => {
@ -51,7 +53,7 @@ module.exports = function(context) {
return fixer.removeRange([ return fixer.removeRange([
// If there's no startNode, we fall back to zero, i.e. start of // If there's no startNode, we fall back to zero, i.e. start of
// file. // file.
startNode ? (startNode.end + 1) : 0, startNode ? startNode.end + 1 : 0,
// We know the function is a block and it'll end with }. Normally // We know the function is a block and it'll end with }. Normally
// there's a new line after that, so just advance past it. This // there's a new line after that, so just advance past it. This
// may be slightly not dodgy in some cases, but covers the existing // may be slightly not dodgy in some cases, but covers the existing
@ -59,7 +61,8 @@ module.exports = function(context) {
node.end + 1, node.end + 1,
]); ]);
}, },
message: "Useless run_test function - only contains run_next_test; whole function can be removed", message:
"Useless run_test function - only contains run_next_test; whole function can be removed",
}); });
} }
}, },

View file

@ -8,7 +8,9 @@
"use strict"; "use strict";
const privilegedGlobals = Object.keys(require("../environments/privileged.js").globals); const privilegedGlobals = Object.keys(
require("../environments/privileged.js").globals
);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Rule Definition // Rule Definition
@ -18,35 +20,40 @@ module.exports = {
meta: { meta: {
messages: { messages: {
unexpectedCall: "Unexpected call to Cu.importGlobalProperties", unexpectedCall: "Unexpected call to Cu.importGlobalProperties",
unexpectedCallWebIdl: "Unnecessary call to Cu.importGlobalProperties (webidl names are automatically imported)", unexpectedCallWebIdl:
"Unnecessary call to Cu.importGlobalProperties (webidl names are automatically imported)",
}, },
schema: [{ schema: [
// XXX Better name? {
"enum": ["everything", "allownonwebidl"], // XXX Better name?
}], enum: ["everything", "allownonwebidl"],
},
],
type: "problem", type: "problem",
}, },
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
if (node.callee.type !== "MemberExpression") { if (node.callee.type !== "MemberExpression") {
return; return;
} }
let memexp = node.callee; let memexp = node.callee;
if (memexp.object.type === "Identifier" && if (
// Only Cu, not Components.utils; see bug 1230369. memexp.object.type === "Identifier" &&
memexp.object.name === "Cu" && // Only Cu, not Components.utils; see bug 1230369.
memexp.property.type === "Identifier" && memexp.object.name === "Cu" &&
memexp.property.name === "importGlobalProperties") { memexp.property.type === "Identifier" &&
memexp.property.name === "importGlobalProperties"
) {
if (context.options.includes("allownonwebidl")) { if (context.options.includes("allownonwebidl")) {
for (let element of node.arguments[0].elements) { for (let element of node.arguments[0].elements) {
if (privilegedGlobals.includes(element.value)) { if (privilegedGlobals.includes(element.value)) {
context.report({ node, messageId: "unexpectedCallWebIdl"}); context.report({ node, messageId: "unexpectedCallWebIdl" });
} }
} }
} else { } else {
context.report({node, messageId: "unexpectedCall"}); context.report({ node, messageId: "unexpectedCall" });
} }
} }
}, },

View file

@ -17,7 +17,7 @@ module.exports = function(context) {
// Public // Public
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
if (typeof(context.options[0]) !== "string") { if (typeof context.options[0] !== "string") {
throw new Error("reject-some-requires expects a regexp"); throw new Error("reject-some-requires expects a regexp");
} }
const RX = new RegExp(context.options[0]); const RX = new RegExp(context.options[0]);
@ -29,17 +29,21 @@ module.exports = function(context) {
}; };
return { return {
"CallExpression": function(node) { CallExpression(node) {
if (node.callee.type == "Identifier" && if (
node.callee.name == "require" && node.callee.type == "Identifier" &&
node.arguments.length == 1 && node.callee.name == "require" &&
node.arguments[0].type == "Literal") { node.arguments.length == 1 &&
node.arguments[0].type == "Literal"
) {
checkPath(node, node.arguments[0].value); checkPath(node, node.arguments[0].value);
} else if (node.callee.type == "MemberExpression" && } else if (
node.callee.property.type == "Identifier" && node.callee.type == "MemberExpression" &&
node.callee.property.name == "lazyRequireGetter" && node.callee.property.type == "Identifier" &&
node.arguments.length >= 3 && node.callee.property.name == "lazyRequireGetter" &&
node.arguments[2].type == "Literal") { node.arguments.length >= 3 &&
node.arguments[2].type == "Literal"
) {
checkPath(node, node.arguments[2].value); checkPath(node, node.arguments[2].value);
} }
}, },

View file

@ -17,13 +17,15 @@ module.exports = {
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
if (node.callee.type === "MemberExpression") { if (node.callee.type === "MemberExpression") {
let memexp = node.callee; let memexp = node.callee;
if (memexp.object.type === "Identifier" && if (
memexp.object.name === "Assert" && memexp.object.type === "Identifier" &&
memexp.property.type === "Identifier" && memexp.object.name === "Assert" &&
memexp.property.name === "rejects") { memexp.property.type === "Identifier" &&
memexp.property.name === "rejects"
) {
// We have ourselves an Assert.rejects. // We have ourselves an Assert.rejects.
if (node.parent.type !== "AwaitExpression") { if (node.parent.type !== "AwaitExpression") {

View file

@ -25,13 +25,19 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"MemberExpression": function(node) { MemberExpression(node) {
if (node.object.type === "Identifier" && if (
node.object.name === "Components" && node.object.type === "Identifier" &&
node.property.type === "Identifier" && node.object.name === "Components" &&
Object.getOwnPropertyNames(componentsMap).includes(node.property.name)) { node.property.type === "Identifier" &&
context.report(node, Object.getOwnPropertyNames(componentsMap).includes(node.property.name)
`Use ${componentsMap[node.property.name]} rather than Components.${node.property.name}`); ) {
context.report(
node,
`Use ${componentsMap[node.property.name]} rather than Components.${
node.property.name
}`
);
} }
}, },
}; };

View file

@ -18,17 +18,19 @@ function isIdentifier(node, id) {
} }
function isMemberExpression(node, object, member) { function isMemberExpression(node, object, member) {
return (node.type === "MemberExpression" && return (
isIdentifier(node.object, object) && node.type === "MemberExpression" &&
isIdentifier(node.property, member)); isIdentifier(node.object, object) &&
isIdentifier(node.property, member)
);
} }
const MSG_NO_JS_QUERY_INTERFACE = ( const MSG_NO_JS_QUERY_INTERFACE =
"Please use ChromeUtils.generateQI rather than manually creating " + "Please use ChromeUtils.generateQI rather than manually creating " +
"JavaScript QueryInterface functions"); "JavaScript QueryInterface functions";
const MSG_NO_XPCOMUTILS_GENERATEQI = ( const MSG_NO_XPCOMUTILS_GENERATEQI =
"Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI"); "Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI";
function funcToGenerateQI(context, node) { function funcToGenerateQI(context, node) {
const sourceCode = context.getSourceCode(); const sourceCode = context.getSourceCode();
@ -41,9 +43,10 @@ function funcToGenerateQI(context, node) {
interfaces.push(match[1] || match[2]); interfaces.push(match[1] || match[2]);
} }
let ifaces = interfaces.filter(iface => iface != "nsISupports") let ifaces = interfaces
.map(iface => JSON.stringify(iface)) .filter(iface => iface != "nsISupports")
.join(", "); .map(iface => JSON.stringify(iface))
.join(", ");
return `ChromeUtils.generateQI([${ifaces}])`; return `ChromeUtils.generateQI([${ifaces}])`;
} }
@ -55,8 +58,8 @@ module.exports = {
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
let {callee} = node; let { callee } = node;
if (isMemberExpression(callee, "XPCOMUtils", "generateQI")) { if (isMemberExpression(callee, "XPCOMUtils", "generateQI")) {
context.report({ context.report({
node, node,
@ -68,8 +71,10 @@ module.exports = {
} }
}, },
"AssignmentExpression > MemberExpression[property.name='QueryInterface']": function(node) { "AssignmentExpression > MemberExpression[property.name='QueryInterface']": function(
const {right} = node.parent; node
) {
const { right } = node.parent;
if (right.type === "FunctionExpression") { if (right.type === "FunctionExpression") {
context.report({ context.report({
node: node.parent, node: node.parent,
@ -81,7 +86,9 @@ module.exports = {
} }
}, },
"Property[key.name='QueryInterface'][value.type='FunctionExpression']": function(node) { "Property[key.name='QueryInterface'][value.type='FunctionExpression']": function(
node
) {
context.report({ context.report({
node, node,
message: MSG_NO_JS_QUERY_INTERFACE, message: MSG_NO_JS_QUERY_INTERFACE,
@ -94,4 +101,3 @@ module.exports = {
}; };
}, },
}; };

View file

@ -18,22 +18,24 @@ function isIdentifier(node, id) {
} }
function isMemberExpression(node, object, member) { function isMemberExpression(node, object, member) {
return (node.type === "MemberExpression" && return (
isIdentifier(node.object, object) && node.type === "MemberExpression" &&
isIdentifier(node.property, member)); isIdentifier(node.object, object) &&
isIdentifier(node.property, member)
);
} }
module.exports = { module.exports = {
meta: { meta: {
schema: [ schema: [
{ {
"type": "object", type: "object",
"properties": { properties: {
"allowCu": { allowCu: {
"type": "boolean", type: "boolean",
}, },
}, },
"additionalProperties": false, additionalProperties: false,
}, },
], ],
fixable: "code", fixable: "code",
@ -41,18 +43,20 @@ module.exports = {
create(context) { create(context) {
return { return {
"CallExpression": function(node) { CallExpression(node) {
if (node.callee.type !== "MemberExpression") { if (node.callee.type !== "MemberExpression") {
return; return;
} }
let {allowCu} = context.options[0] || {}; let { allowCu } = context.options[0] || {};
let {callee} = node; let { callee } = node;
// Is the expression starting with `Cu` or `Components.utils`? // Is the expression starting with `Cu` or `Components.utils`?
if (((!allowCu && isIdentifier(callee.object, "Cu")) || if (
isMemberExpression(callee.object, "Components", "utils")) && ((!allowCu && isIdentifier(callee.object, "Cu")) ||
isIdentifier(callee.property, "import")) { isMemberExpression(callee.object, "Components", "utils")) &&
isIdentifier(callee.property, "import")
) {
context.report({ context.report({
node, node,
message: "Please use ChromeUtils.import instead of Cu.import", message: "Please use ChromeUtils.import instead of Cu.import",
@ -62,14 +66,20 @@ module.exports = {
}); });
} }
if (isMemberExpression(callee, "XPCOMUtils", "defineLazyModuleGetter") && if (
node.arguments.length < 4) { isMemberExpression(callee, "XPCOMUtils", "defineLazyModuleGetter") &&
node.arguments.length < 4
) {
context.report({ context.report({
node, node,
message: ("Please use ChromeUtils.defineModuleGetter instead of " + message:
"XPCOMUtils.defineLazyModuleGetter"), "Please use ChromeUtils.defineModuleGetter instead of " +
"XPCOMUtils.defineLazyModuleGetter",
fix(fixer) { fix(fixer) {
return fixer.replaceText(callee, "ChromeUtils.defineModuleGetter"); return fixer.replaceText(
callee,
"ChromeUtils.defineModuleGetter"
);
}, },
}); });
} }

View file

@ -19,21 +19,22 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"TryStatement": function(node) { TryStatement(node) {
let types = ["Bool", "Char", "Float", "Int"]; let types = ["Bool", "Char", "Float", "Int"];
let methods = types.map(type => "get" + type + "Pref"); let methods = types.map(type => "get" + type + "Pref");
if (node.block.type != "BlockStatement" || if (node.block.type != "BlockStatement" || node.block.body.length != 1) {
node.block.body.length != 1) {
return; return;
} }
let firstStm = node.block.body[0]; let firstStm = node.block.body[0];
if (firstStm.type != "ExpressionStatement" || if (
firstStm.expression.type != "AssignmentExpression" || firstStm.type != "ExpressionStatement" ||
firstStm.expression.right.type != "CallExpression" || firstStm.expression.type != "AssignmentExpression" ||
firstStm.expression.right.callee.type != "MemberExpression" || firstStm.expression.right.type != "CallExpression" ||
firstStm.expression.right.callee.property.type != "Identifier" || firstStm.expression.right.callee.type != "MemberExpression" ||
!methods.includes(firstStm.expression.right.callee.property.name)) { firstStm.expression.right.callee.property.type != "Identifier" ||
!methods.includes(firstStm.expression.right.callee.property.name)
) {
return; return;
} }

View file

@ -18,22 +18,26 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"BinaryExpression": function(node) { BinaryExpression(node) {
if (node.left.type != "CallExpression" || if (
node.left.callee.type != "MemberExpression" || node.left.type != "CallExpression" ||
node.left.callee.property.type != "Identifier" || node.left.callee.type != "MemberExpression" ||
node.left.callee.property.name != "indexOf") { node.left.callee.property.type != "Identifier" ||
node.left.callee.property.name != "indexOf"
) {
return; return;
} }
if ((["!=", "!==", "==", "==="].includes(node.operator) && if (
node.right.type == "UnaryExpression" && (["!=", "!==", "==", "==="].includes(node.operator) &&
node.right.operator == "-" && node.right.type == "UnaryExpression" &&
node.right.argument.type == "Literal" && node.right.operator == "-" &&
node.right.argument.value == 1) || node.right.argument.type == "Literal" &&
([">=", "<"].includes(node.operator) && node.right.argument.value == 1) ||
node.right.type == "Literal" && ([">=", "<"].includes(node.operator) &&
node.right.value == 0)) { node.right.type == "Literal" &&
node.right.value == 0)
) {
context.report(node, "use .includes instead of .indexOf"); context.report(node, "use .includes instead of .indexOf");
} }
}, },

View file

@ -18,17 +18,21 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"MemberExpression": function(node) { MemberExpression(node) {
if (node.property.type != "Identifier" || if (
node.property.name != "defaultView" || node.property.type != "Identifier" ||
node.object.type != "MemberExpression" || node.property.name != "defaultView" ||
node.object.property.type != "Identifier" || node.object.type != "MemberExpression" ||
node.object.property.name != "ownerDocument") { node.object.property.type != "Identifier" ||
node.object.property.name != "ownerDocument"
) {
return; return;
} }
context.report(node, context.report(
"use .ownerGlobal instead of .ownerDocument.defaultView"); node,
"use .ownerGlobal instead of .ownerDocument.defaultView"
);
}, },
}; };
}; };

View file

@ -18,21 +18,27 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"ExpressionStatement": function(node) { ExpressionStatement(node) {
if (!node.expression || if (
node.expression.type != "CallExpression" || !node.expression ||
!node.expression.callee || node.expression.type != "CallExpression" ||
node.expression.callee.type != "MemberExpression" || !node.expression.callee ||
!node.expression.callee.property || node.expression.callee.type != "MemberExpression" ||
node.expression.callee.property.type != "Identifier" || !node.expression.callee.property ||
(node.expression.callee.property.name != "concat" && node.expression.callee.property.type != "Identifier" ||
node.expression.callee.property.name != "join" && (node.expression.callee.property.name != "concat" &&
node.expression.callee.property.name != "slice")) { node.expression.callee.property.name != "join" &&
node.expression.callee.property.name != "slice")
) {
return; return;
} }
context.report(node, context.report(
`{Array/String}.${node.expression.callee.property.name} doesn't modify the instance in-place`); node,
`{Array/String}.${
node.expression.callee.property.name
} doesn't modify the instance in-place`
);
}, },
}; };
}; };

View file

@ -22,16 +22,16 @@ var servicesASTParser = {
identifiers: {}, identifiers: {},
// These interfaces are difficult/not possible to get via processing. // These interfaces are difficult/not possible to get via processing.
result: { result: {
"nsIPrefBranch": "prefs", nsIPrefBranch: "prefs",
"nsIPrefService": "prefs", nsIPrefService: "prefs",
"nsIXULRuntime": "appinfo", nsIXULRuntime: "appinfo",
"nsIXULAppInfo": "appinfo", nsIXULAppInfo: "appinfo",
"nsIDirectoryService": "dirsvc", nsIDirectoryService: "dirsvc",
"nsIProperties": "dirsvc", nsIProperties: "dirsvc",
"nsIIOService": "io", nsIIOService: "io",
"nsISpeculativeConnect": "io", nsISpeculativeConnect: "io",
"nsICookieManager": "cookies", nsICookieManager: "cookies",
"nsIBlocklistService": "blocklist", nsIBlocklistService: "blocklist",
}, },
/** /**
@ -39,10 +39,12 @@ var servicesASTParser = {
* with objects, and records the assumed interface definitions. * with objects, and records the assumed interface definitions.
*/ */
VariableDeclaration(node, parents) { VariableDeclaration(node, parents) {
if (node.declarations.length === 1 && if (
node.declarations[0].id && node.declarations.length === 1 &&
helpers.getIsGlobalScope(parents) && node.declarations[0].id &&
node.declarations[0].init.type === "ObjectExpression") { helpers.getIsGlobalScope(parents) &&
node.declarations[0].init.type === "ObjectExpression"
) {
let name = node.declarations[0].id.name; let name = node.declarations[0].id.name;
let interfaces = {}; let interfaces = {};
@ -61,13 +63,16 @@ var servicesASTParser = {
* them to the identifier tables created by the VariableDeclaration calls. * them to the identifier tables created by the VariableDeclaration calls.
*/ */
AssignmentExpression(node, parents) { AssignmentExpression(node, parents) {
if (node.left.type === "MemberExpression" && if (
node.right.type === "ArrayExpression" && node.left.type === "MemberExpression" &&
helpers.getIsGlobalScope(parents)) { node.right.type === "ArrayExpression" &&
helpers.getIsGlobalScope(parents)
) {
let variableName = node.left.object.name; let variableName = node.left.object.name;
if (variableName in this.identifiers) { if (variableName in this.identifiers) {
let servicesPropName = node.left.property.name; let servicesPropName = node.left.property.name;
this.identifiers[variableName][servicesPropName] = node.right.elements[1].value; this.identifiers[variableName][servicesPropName] =
node.right.elements[1].value;
} }
} }
}, },
@ -78,11 +83,13 @@ var servicesASTParser = {
* Services.jsm is caching. * Services.jsm is caching.
*/ */
CallExpression(node) { CallExpression(node) {
if (node.callee.object && if (
node.callee.object.name === "XPCOMUtils" && node.callee.object &&
node.callee.property && node.callee.object.name === "XPCOMUtils" &&
node.callee.property.name === "defineLazyServiceGetters" && node.callee.property &&
node.arguments.length >= 2) { node.callee.property.name === "defineLazyServiceGetters" &&
node.arguments.length >= 2
) {
// The second argument has the getters name. // The second argument has the getters name.
let gettersVarName = node.arguments[1].name; let gettersVarName = node.arguments[1].name;
if (!(gettersVarName in this.identifiers)) { if (!(gettersVarName in this.identifiers)) {
@ -97,7 +104,12 @@ var servicesASTParser = {
}; };
function getInterfacesFromServicesFile() { function getInterfacesFromServicesFile() {
let filePath = path.join(helpers.rootDir, "toolkit", "modules", "Services.jsm"); let filePath = path.join(
helpers.rootDir,
"toolkit",
"modules",
"Services.jsm"
);
let content = fs.readFileSync(filePath, "utf8"); let content = fs.readFileSync(filePath, "utf8");
@ -120,9 +132,9 @@ function getInterfacesFromServicesFile() {
return servicesASTParser.result; return servicesASTParser.result;
} }
let getServicesInterfaceMap = helpers.isMozillaCentralBased() ? let getServicesInterfaceMap = helpers.isMozillaCentralBased()
getInterfacesFromServicesFile() : ? getInterfacesFromServicesFile()
helpers.getSavedRuleData("use-services.js"); : helpers.getSavedRuleData("use-services.js");
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Rule Definition // Rule Definition
@ -141,22 +153,27 @@ module.exports = function(context) {
}, },
CallExpression(node) { CallExpression(node) {
if (!node.callee || if (
!node.callee.property || !node.callee ||
node.callee.property.type != "Identifier" || !node.callee.property ||
node.callee.property.name != "getService" || node.callee.property.type != "Identifier" ||
node.arguments.length != 1 || node.callee.property.name != "getService" ||
!node.arguments[0].property || node.arguments.length != 1 ||
node.arguments[0].property.type != "Identifier" || !node.arguments[0].property ||
!node.arguments[0].property.name || node.arguments[0].property.type != "Identifier" ||
!(node.arguments[0].property.name in getServicesInterfaceMap)) { !node.arguments[0].property.name ||
!(node.arguments[0].property.name in getServicesInterfaceMap)
) {
return; return;
} }
let serviceName = getServicesInterfaceMap[node.arguments[0].property.name]; let serviceName =
getServicesInterfaceMap[node.arguments[0].property.name];
context.report(node, context.report(
`Use Services.${serviceName} rather than getService().`); node,
`Use Services.${serviceName} rather than getService().`
);
}, },
}; };
}; };

View file

@ -21,7 +21,7 @@ module.exports = function(context) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
return { return {
"VariableDeclaration": function(node) { VariableDeclaration(node) {
if (node.kind === "var") { if (node.kind === "var") {
if (helpers.getIsGlobalScope(context.getAncestors())) { if (helpers.getIsGlobalScope(context.getAncestors())) {
return; return;

View file

@ -29,7 +29,11 @@ function MozillaFormatter(runner) {
failures++; failures++;
// Replace any newlines in the title. // Replace any newlines in the title.
let title = test.title.replace(/\n/g, "|"); let title = test.title.replace(/\n/g, "|");
console.log(`TEST-UNEXPECTED-FAIL | ${path.basename(test.file)} | ${title} | ${err.message}`); console.log(
`TEST-UNEXPECTED-FAIL | ${path.basename(test.file)} | ${title} | ${
err.message
}`
);
}); });
runner.on("end", function() { runner.on("end", function() {

View file

@ -10,19 +10,32 @@ var fs = require("fs");
var path = require("path"); var path = require("path");
var helpers = require("../lib/helpers"); var helpers = require("../lib/helpers");
const eslintDir = path.join(helpers.rootDir, const eslintDir = path.join(helpers.rootDir, "tools", "lint", "eslint");
"tools", "lint", "eslint");
const globalsFile = path.join(eslintDir, "eslint-plugin-mozilla", const globalsFile = path.join(
"lib", "environments", "saved-globals.json"); eslintDir,
const rulesFile = path.join(eslintDir, "eslint-plugin-mozilla", "eslint-plugin-mozilla",
"lib", "rules", "saved-rules-data.json"); "lib",
"environments",
"saved-globals.json"
);
const rulesFile = path.join(
eslintDir,
"eslint-plugin-mozilla",
"lib",
"rules",
"saved-rules-data.json"
);
console.log("Copying modules.json"); console.log("Copying modules.json");
const modulesFile = path.join(eslintDir, "modules.json"); const modulesFile = path.join(eslintDir, "modules.json");
const shipModulesFile = path.join(eslintDir, "eslint-plugin-mozilla", "lib", const shipModulesFile = path.join(
"modules.json"); eslintDir,
"eslint-plugin-mozilla",
"lib",
"modules.json"
);
fs.writeFileSync(shipModulesFile, fs.readFileSync(modulesFile)); fs.writeFileSync(shipModulesFile, fs.readFileSync(modulesFile));
@ -31,26 +44,30 @@ console.log("Generating globals file");
// Export the environments. // Export the environments.
let environmentGlobals = require("../lib/index.js").environments; let environmentGlobals = require("../lib/index.js").environments;
return fs.writeFile(globalsFile, JSON.stringify({environments: environmentGlobals}), err => { return fs.writeFile(
if (err) { globalsFile,
console.error(err); JSON.stringify({ environments: environmentGlobals }),
process.exit(1); err => {
} if (err) {
console.error(err);
console.log("Globals file generation complete");
console.log("Creating rules data file");
// Also export data for the use-services.js rule
let rulesData = {
"use-services.js": require("../lib/rules/use-services.js")().getServicesInterfaceMap(),
};
return fs.writeFile(rulesFile, JSON.stringify({rulesData}), err1 => {
if (err1) {
console.error(err1);
process.exit(1); process.exit(1);
} }
console.log("Globals file generation complete"); console.log("Globals file generation complete");
});
}); console.log("Creating rules data file");
// Also export data for the use-services.js rule
let rulesData = {
"use-services.js": require("../lib/rules/use-services.js")().getServicesInterfaceMap(),
};
return fs.writeFile(rulesFile, JSON.stringify({ rulesData }), err1 => {
if (err1) {
console.error(err1);
process.exit(1);
}
console.log("Globals file generation complete");
});
}
);

View file

@ -17,7 +17,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code, type, message) { function invalidCode(code, type, message) {
return {code, errors: [{message, type}]}; return { code, errors: [{ message, type }] };
} }
ruleTester.run("avoid-Date-timing", rule, { ruleTester.run("avoid-Date-timing", rule, {
@ -28,12 +28,15 @@ ruleTester.run("avoid-Date-timing", rule, {
"Date.UTC(2017, 7);", "Date.UTC(2017, 7);",
], ],
invalid: [ invalid: [
invalidCode("Date.now();", "CallExpression", invalidCode(
"use performance.now() instead of Date.now() " + "Date.now();",
"for timing measurements"), "CallExpression",
invalidCode("new Date();", "NewExpression", "use performance.now() instead of Date.now() " + "for timing measurements"
"use performance.now() instead of new Date() " + ),
"for timing measurements"), invalidCode(
"new Date();",
"NewExpression",
"use performance.now() instead of new Date() " + "for timing measurements"
),
], ],
}); });

View file

@ -18,10 +18,11 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code, message) { function invalidCode(code, message) {
if (!message) { if (!message) {
message = "use element.remove() instead of " + message =
"element.parentNode.removeChild(element)"; "use element.remove() instead of " +
"element.parentNode.removeChild(element)";
} }
return {code, errors: [{message, type: "CallExpression"}]}; return { code, errors: [{ message, type: "CallExpression" }] };
} }
ruleTester.run("avoid-removeChild", rule, { ruleTester.run("avoid-removeChild", rule, {
@ -36,8 +37,10 @@ ruleTester.run("avoid-removeChild", rule, {
invalidCode("elt.parentNode.parentNode.removeChild(elt.parentNode);"), invalidCode("elt.parentNode.parentNode.removeChild(elt.parentNode);"),
invalidCode("$(e).parentNode.removeChild($(e));"), invalidCode("$(e).parentNode.removeChild($(e));"),
invalidCode("$('e').parentNode.removeChild($('e'));"), invalidCode("$('e').parentNode.removeChild($('e'));"),
invalidCode("elt.removeChild(elt.firstChild);", invalidCode(
"use element.firstChild.remove() instead of " + "elt.removeChild(elt.firstChild);",
"element.removeChild(element.firstChild)"), "use element.firstChild.remove() instead of " +
"element.removeChild(element.firstChild)"
),
], ],
}); });

View file

@ -19,61 +19,71 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function error(code, message) { function error(code, message) {
return { return {
code, code,
errors: [{message, type: "Identifier"}], errors: [{ message, type: "Identifier" }],
}; };
} }
ruleTester.run("balanced-listeners", rule, { ruleTester.run("balanced-listeners", rule, {
valid: [ valid: [
"elt.addEventListener('event', handler);" + "elt.addEventListener('event', handler);" +
"elt.removeEventListener('event', handler);", "elt.removeEventListener('event', handler);",
"elt.addEventListener('event', handler, true);" + "elt.addEventListener('event', handler, true);" +
"elt.removeEventListener('event', handler, true);", "elt.removeEventListener('event', handler, true);",
"elt.addEventListener('event', handler, false);" + "elt.addEventListener('event', handler, false);" +
"elt.removeEventListener('event', handler, false);", "elt.removeEventListener('event', handler, false);",
"elt.addEventListener('event', handler);" + "elt.addEventListener('event', handler);" +
"elt.removeEventListener('event', handler, false);", "elt.removeEventListener('event', handler, false);",
"elt.addEventListener('event', handler, false);" + "elt.addEventListener('event', handler, false);" +
"elt.removeEventListener('event', handler);", "elt.removeEventListener('event', handler);",
"elt.addEventListener('event', handler, {capture: false});" + "elt.addEventListener('event', handler, {capture: false});" +
"elt.removeEventListener('event', handler);", "elt.removeEventListener('event', handler);",
"elt.addEventListener('event', handler);" + "elt.addEventListener('event', handler);" +
"elt.removeEventListener('event', handler, {capture: false});", "elt.removeEventListener('event', handler, {capture: false});",
"elt.addEventListener('event', handler, {capture: true});" + "elt.addEventListener('event', handler, {capture: true});" +
"elt.removeEventListener('event', handler, true);", "elt.removeEventListener('event', handler, true);",
"elt.addEventListener('event', handler, true);" + "elt.addEventListener('event', handler, true);" +
"elt.removeEventListener('event', handler, {capture: true});", "elt.removeEventListener('event', handler, {capture: true});",
"elt.addEventListener('event', handler, {once: true});", "elt.addEventListener('event', handler, {once: true});",
"elt.addEventListener('event', handler, {once: true, capture: true});", "elt.addEventListener('event', handler, {once: true, capture: true});",
], ],
invalid: [ invalid: [
error("elt.addEventListener('click', handler, false);", error(
"No corresponding 'removeEventListener(click)' was found."), "elt.addEventListener('click', handler, false);",
"No corresponding 'removeEventListener(click)' was found."
),
error("elt.addEventListener('click', handler, false);" + error(
"elt.removeEventListener('click', handler, true);", "elt.addEventListener('click', handler, false);" +
"No corresponding 'removeEventListener(click)' was found."), "elt.removeEventListener('click', handler, true);",
"No corresponding 'removeEventListener(click)' was found."
),
error("elt.addEventListener('click', handler, {capture: false});" + error(
"elt.removeEventListener('click', handler, true);", "elt.addEventListener('click', handler, {capture: false});" +
"No corresponding 'removeEventListener(click)' was found."), "elt.removeEventListener('click', handler, true);",
"No corresponding 'removeEventListener(click)' was found."
),
error("elt.addEventListener('click', handler, {capture: true});" + error(
"elt.removeEventListener('click', handler);", "elt.addEventListener('click', handler, {capture: true});" +
"No corresponding 'removeEventListener(click)' was found."), "elt.removeEventListener('click', handler);",
"No corresponding 'removeEventListener(click)' was found."
),
error("elt.addEventListener('click', handler, true);" + error(
"elt.removeEventListener('click', handler);", "elt.addEventListener('click', handler, true);" +
"No corresponding 'removeEventListener(click)' was found."), "elt.removeEventListener('click', handler);",
"No corresponding 'removeEventListener(click)' was found."
),
], ],
}); });

View file

@ -17,7 +17,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 8 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code) { function invalidCode(code) {
return {code, errors: [{messageId: "consistentIfBracing"}]}; return { code, errors: [{ messageId: "consistentIfBracing" }] };
} }
ruleTester.run("consistent-if-bracing", rule, { ruleTester.run("consistent-if-bracing", rule, {

View file

@ -17,7 +17,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code, type, message) { function invalidCode(code, type, message) {
return {code, errors: [{message, type}]}; return { code, errors: [{ message, type }] };
} }
ruleTester.run("mark-exported-symbols-as-used", rule, { ruleTester.run("mark-exported-symbols-as-used", rule, {
@ -26,11 +26,20 @@ ruleTester.run("mark-exported-symbols-as-used", rule, {
"this.EXPORTED_SYMBOLS = ['foo'];", "this.EXPORTED_SYMBOLS = ['foo'];",
], ],
invalid: [ invalid: [
invalidCode("let EXPORTED_SYMBOLS = ['foo'];", "VariableDeclaration", invalidCode(
"EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`"), "let EXPORTED_SYMBOLS = ['foo'];",
invalidCode("var EXPORTED_SYMBOLS = 'foo';", "VariableDeclaration", "VariableDeclaration",
"Unexpected assignment of non-Array to EXPORTED_SYMBOLS"), "EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`"
invalidCode("this.EXPORTED_SYMBOLS = 'foo';", "AssignmentExpression", ),
"Unexpected assignment of non-Array to EXPORTED_SYMBOLS"), invalidCode(
"var EXPORTED_SYMBOLS = 'foo';",
"VariableDeclaration",
"Unexpected assignment of non-Array to EXPORTED_SYMBOLS"
),
invalidCode(
"this.EXPORTED_SYMBOLS = 'foo';",
"AssignmentExpression",
"Unexpected assignment of non-Array to EXPORTED_SYMBOLS"
),
], ],
}); });

View file

@ -17,13 +17,14 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function wrapCode(code, filename = "xpcshell/test_foo.js") { function wrapCode(code, filename = "xpcshell/test_foo.js") {
return {code, filename}; return { code, filename };
} }
function invalidCode(code) { function invalidCode(code) {
let message = "listen for events instead of setTimeout() with arbitrary delay"; let message =
"listen for events instead of setTimeout() with arbitrary delay";
let obj = wrapCode(code); let obj = wrapCode(code);
obj.errors = [{message, type: "CallExpression"}]; obj.errors = [{ message, type: "CallExpression" }];
return obj; return obj;
} }
@ -38,4 +39,3 @@ ruleTester.run("no-arbitrary-setTimeout", rule, {
invalidCode("setTimeout(function() {}, timeout);"), invalidCode("setTimeout(function() {}, timeout);"),
], ],
}); });

View file

@ -17,16 +17,13 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function callError(message) { function callError(message) {
return [{message, type: "BinaryExpression"}]; return [{ message, type: "BinaryExpression" }];
} }
const MESSAGE = "Don't compare for inexact equality against boolean literals"; const MESSAGE = "Don't compare for inexact equality against boolean literals";
ruleTester.run("no-compare-against-boolean-literals", rule, { ruleTester.run("no-compare-against-boolean-literals", rule, {
valid: [ valid: [`if (!foo) {}`, `if (!!foo) {}`],
`if (!foo) {}`,
`if (!!foo) {}`,
],
invalid: [ invalid: [
{ {
code: `if (foo == true) {}`, code: `if (foo == true) {}`,
@ -62,4 +59,3 @@ ruleTester.run("no-compare-against-boolean-literals", rule, {
}, },
], ],
}); });

View file

@ -21,10 +21,13 @@ function invalidCode(code, varNames) {
varNames = [varNames]; varNames = [varNames];
} }
return { return {
code, errors: varNames.map(name => [{ code,
message: `${name} is now defined in global scope, a separate definition is no longer necessary.`, errors: varNames.map(name => [
type: "VariableDeclarator", {
}]), message: `${name} is now defined in global scope, a separate definition is no longer necessary.`,
type: "VariableDeclarator",
},
]),
}; };
} }
@ -47,7 +50,13 @@ ruleTester.run("no-define-cc-etc", rule, {
invalidCode("const {classes: Cc} = Components;", "Cc"), invalidCode("const {classes: Cc} = Components;", "Cc"),
invalidCode("let {classes: Cc, manager: Cm} = Components", "Cc"), invalidCode("let {classes: Cc, manager: Cm} = Components", "Cc"),
invalidCode("const Cu = Components.utils;", "Cu"), invalidCode("const Cu = Components.utils;", "Cu"),
invalidCode("var Ci = Components.interfaces, Cc = Components.classes;", ["Ci", "Cc"]), invalidCode("var Ci = Components.interfaces, Cc = Components.classes;", [
invalidCode("var {'interfaces': Ci, 'classes': Cc} = Components;", ["Ci", "Cc"]), "Ci",
"Cc",
]),
invalidCode("var {'interfaces': Ci, 'classes': Cc} = Components;", [
"Ci",
"Cc",
]),
], ],
}); });

View file

@ -17,7 +17,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function callError(message) { function callError(message) {
return [{message, type: "CallExpression"}]; return [{ message, type: "CallExpression" }];
} }
ruleTester.run("no-useless-parameters", rule, { ruleTester.run("no-useless-parameters", rule, {
@ -91,47 +91,47 @@ ruleTester.run("no-useless-parameters", rule, {
code: "elt.addEventListener('click', handler, false);", code: "elt.addEventListener('click', handler, false);",
output: "elt.addEventListener('click', handler);", output: "elt.addEventListener('click', handler);",
errors: callError( errors: callError(
"addEventListener's third parameter can be omitted when it's false."), "addEventListener's third parameter can be omitted when it's false."
),
}, },
{ {
code: "elt.removeEventListener('click', handler, false);", code: "elt.removeEventListener('click', handler, false);",
output: "elt.removeEventListener('click', handler);", output: "elt.removeEventListener('click', handler);",
errors: callError( errors: callError(
"removeEventListener's third parameter can be omitted when it's" + "removeEventListener's third parameter can be omitted when it's" +
" false."), " false."
),
}, },
{ {
code: "Services.obs.addObserver(this, 'topic', false);", code: "Services.obs.addObserver(this, 'topic', false);",
output: "Services.obs.addObserver(this, 'topic');", output: "Services.obs.addObserver(this, 'topic');",
errors: callError( errors: callError(
"addObserver's third parameter can be omitted when it's" + "addObserver's third parameter can be omitted when it's" + " false."
" false."), ),
}, },
{ {
code: "Services.prefs.addObserver('branch', this, false);", code: "Services.prefs.addObserver('branch', this, false);",
output: "Services.prefs.addObserver('branch', this);", output: "Services.prefs.addObserver('branch', this);",
errors: callError( errors: callError(
"addObserver's third parameter can be omitted when it's" + "addObserver's third parameter can be omitted when it's" + " false."
" false."), ),
}, },
{ {
code: "array.appendElement(elt, false);", code: "array.appendElement(elt, false);",
output: "array.appendElement(elt);", output: "array.appendElement(elt);",
errors: callError( errors: callError(
"appendElement's second parameter can be omitted when it's" + "appendElement's second parameter can be omitted when it's" + " false."
" false."), ),
}, },
{ {
code: "Services.obs.notifyObservers(obj, 'topic', null);", code: "Services.obs.notifyObservers(obj, 'topic', null);",
output: "Services.obs.notifyObservers(obj, 'topic');", output: "Services.obs.notifyObservers(obj, 'topic');",
errors: callError( errors: callError("notifyObservers's third parameter can be omitted."),
"notifyObservers's third parameter can be omitted."),
}, },
{ {
code: "Services.obs.notifyObservers(obj, 'topic', '');", code: "Services.obs.notifyObservers(obj, 'topic', '');",
output: "Services.obs.notifyObservers(obj, 'topic');", output: "Services.obs.notifyObservers(obj, 'topic');",
errors: callError( errors: callError("notifyObservers's third parameter can be omitted."),
"notifyObservers's third parameter can be omitted."),
}, },
{ {
code: "window.getComputedStyle(elt, null);", code: "window.getComputedStyle(elt, null);",

View file

@ -17,9 +17,10 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code) { function invalidCode(code) {
let message = "use {once: true} instead of removeEventListener " + let message =
"as the first instruction of the listener"; "use {once: true} instead of removeEventListener " +
return {code, errors: [{message, type: "CallExpression"}]}; "as the first instruction of the listener";
return { code, errors: [{ message, type: "CallExpression" }] };
} }
ruleTester.run("no-useless-removeEventListener", rule, { ruleTester.run("no-useless-removeEventListener", rule, {
@ -34,53 +35,65 @@ ruleTester.run("no-useless-removeEventListener", rule, {
// Should not reject when removing a listener for another event. // Should not reject when removing a listener for another event.
"elt.addEventListener('click', function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.removeEventListener('keypress', listener);" + " elt.removeEventListener('keypress', listener);" +
"});", "});",
// Should not reject when there's another instruction before // Should not reject when there's another instruction before
// removeEventListener. // removeEventListener.
"elt.addEventListener('click', function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.focus();" + " elt.focus();" +
" elt.removeEventListener('click', listener);" + " elt.removeEventListener('click', listener);" +
"});", "});",
// Should not reject when wantsUntrusted is true. // Should not reject when wantsUntrusted is true.
"elt.addEventListener('click', function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.removeEventListener('click', listener);" + " elt.removeEventListener('click', listener);" +
"}, false, true);", "}, false, true);",
// Should not reject when there's a literal and a variable // Should not reject when there's a literal and a variable
"elt.addEventListener('click', function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.removeEventListener(eventName, listener);" + " elt.removeEventListener(eventName, listener);" +
"});", "});",
// Should not reject when there's 2 different variables // Should not reject when there's 2 different variables
"elt.addEventListener(event1, function listener() {" + "elt.addEventListener(event1, function listener() {" +
" elt.removeEventListener(event2, listener);" + " elt.removeEventListener(event2, listener);" +
"});", "});",
// Should not fail if this is a different type of event listener function. // Should not fail if this is a different type of event listener function.
"myfunc.addEventListener(listener);", "myfunc.addEventListener(listener);",
], ],
invalid: [ invalid: [
invalidCode("elt.addEventListener('click', function listener() {" + invalidCode(
" elt.removeEventListener('click', listener);" + "elt.addEventListener('click', function listener() {" +
"});"), " elt.removeEventListener('click', listener);" +
invalidCode("elt.addEventListener('click', function listener() {" + "});"
" elt.removeEventListener('click', listener, true);" + ),
"}, true);"), invalidCode(
invalidCode("elt.addEventListener('click', function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.removeEventListener('click', listener);" + " elt.removeEventListener('click', listener, true);" +
"}, {once: true});"), "}, true);"
invalidCode("elt.addEventListener('click', function listener() {" + ),
" /* Comment */" + invalidCode(
" elt.removeEventListener('click', listener);" + "elt.addEventListener('click', function listener() {" +
"});"), " elt.removeEventListener('click', listener);" +
invalidCode("elt.addEventListener('click', function() {" + "}, {once: true});"
" elt.removeEventListener('click', arguments.callee);" + ),
"});"), invalidCode(
invalidCode("elt.addEventListener(eventName, function listener() {" + "elt.addEventListener('click', function listener() {" +
" elt.removeEventListener(eventName, listener);" + " /* Comment */" +
"});"), " elt.removeEventListener('click', listener);" +
"});"
),
invalidCode(
"elt.addEventListener('click', function() {" +
" elt.removeEventListener('click', arguments.callee);" +
"});"
),
invalidCode(
"elt.addEventListener(eventName, function listener() {" +
" elt.removeEventListener(eventName, listener);" +
"});"
),
], ],
}); });

View file

@ -19,7 +19,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code, output) { function invalidCode(code, output) {
let message = let message =
"Useless run_test function - only contains run_next_test; whole function can be removed"; "Useless run_test function - only contains run_next_test; whole function can be removed";
return {code, output, errors: [{message, type: "FunctionDeclaration"}]}; return { code, output, errors: [{ message, type: "FunctionDeclaration" }] };
} }
ruleTester.run("no-useless-run-test", rule, { ruleTester.run("no-useless-run-test", rule, {
@ -30,16 +30,17 @@ ruleTester.run("no-useless-run-test", rule, {
], ],
invalid: [ invalid: [
// Single-line case. // Single-line case.
invalidCode( invalidCode("function run_test() { run_next_test(); }", ""),
"function run_test() { run_next_test(); }",
""),
// Multiple-line cases // Multiple-line cases
invalidCode(` invalidCode(
`
function run_test() { function run_test() {
run_next_test(); run_next_test();
}`, }`,
``), ``
invalidCode(` ),
invalidCode(
`
foo(); foo();
function run_test() { function run_test() {
run_next_test(); run_next_test();
@ -47,8 +48,10 @@ function run_test() {
`, `,
` `
foo(); foo();
`), `
invalidCode(` ),
invalidCode(
`
foo(); foo();
function run_test() { function run_test() {
run_next_test(); run_next_test();
@ -58,8 +61,10 @@ bar();
` `
foo(); foo();
bar(); bar();
`), `
invalidCode(` ),
invalidCode(
`
foo(); foo();
function run_test() { function run_test() {
@ -70,8 +75,10 @@ bar();`,
` `
foo(); foo();
bar();`), bar();`
invalidCode(` ),
invalidCode(
`
foo(); foo();
function run_test() { function run_test() {
@ -86,8 +93,10 @@ foo();
// A comment // A comment
bar(); bar();
`), `
invalidCode(` ),
invalidCode(
`
foo(); foo();
/** /**
@ -109,6 +118,7 @@ foo();
// A comment // A comment
bar(); bar();
`), `
),
], ],
}); });

View file

@ -17,23 +17,30 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 8 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
ruleTester.run("reject-importGlobalProperties", rule, { ruleTester.run("reject-importGlobalProperties", rule, {
valid: [{ valid: [
code: "Cu.something();", {
}, { code: "Cu.something();",
options: ["allownonwebidl"], },
code: "Cu.importGlobalProperties(['fetch'])", {
}], options: ["allownonwebidl"],
invalid: [{ code: "Cu.importGlobalProperties(['fetch'])",
code: "Cu.importGlobalProperties(['fetch'])", },
options: ["everything"], ],
errors: [{ messageId: "unexpectedCall"}], invalid: [
}, { {
code: "Cu.importGlobalProperties(['TextEncoder'])", code: "Cu.importGlobalProperties(['fetch'])",
options: ["everything"], options: ["everything"],
errors: [{ messageId: "unexpectedCall"}], errors: [{ messageId: "unexpectedCall" }],
}, { },
code: "Cu.importGlobalProperties(['TextEncoder'])", {
options: ["allownonwebidl"], code: "Cu.importGlobalProperties(['TextEncoder'])",
errors: [{ messageId: "unexpectedCallWebIdl"}], options: ["everything"],
}], errors: [{ messageId: "unexpectedCall" }],
},
{
code: "Cu.importGlobalProperties(['TextEncoder'])",
options: ["allownonwebidl"],
errors: [{ messageId: "unexpectedCallWebIdl" }],
},
],
}); });

View file

@ -17,7 +17,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 8 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code, messageId) { function invalidCode(code, messageId) {
return {code, errors: [{messageId: "rejectRequiresAwait"}]}; return { code, errors: [{ messageId: "rejectRequiresAwait" }] };
} }
ruleTester.run("reject-requires-await", rule, { ruleTester.run("reject-requires-await", rule, {

View file

@ -17,21 +17,39 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function invalidCode(code, originalName, newName) { function invalidCode(code, originalName, newName) {
return {code, errors: [{ return {
message: `Use ${newName} rather than ${originalName}`, code,
type: "MemberExpression", errors: [
}]}; {
message: `Use ${newName} rather than ${originalName}`,
type: "MemberExpression",
},
],
};
} }
ruleTester.run("use-cc-etc", rule, { ruleTester.run("use-cc-etc", rule, {
valid: [ valid: ["Components.Constructor();", "let x = Components.foo;"],
"Components.Constructor();",
"let x = Components.foo;",
],
invalid: [ invalid: [
invalidCode("let foo = Components.classes['bar'];", "Components.classes", "Cc"), invalidCode(
invalidCode("let bar = Components.interfaces.bar;", "Components.interfaces", "Ci"), "let foo = Components.classes['bar'];",
invalidCode("Components.results.NS_ERROR_ILLEGAL_INPUT;", "Components.results", "Cr"), "Components.classes",
invalidCode("Components.utils.reportError('fake');", "Components.utils", "Cu"), "Cc"
),
invalidCode(
"let bar = Components.interfaces.bar;",
"Components.interfaces",
"Ci"
),
invalidCode(
"Components.results.NS_ERROR_ILLEGAL_INPUT;",
"Components.results",
"Cr"
),
invalidCode(
"Components.utils.reportError('fake');",
"Components.utils",
"Cu"
),
], ],
}); });

View file

@ -17,25 +17,27 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function callError(message) { function callError(message) {
return [{message, type: "CallExpression"}]; return [{ message, type: "CallExpression" }];
} }
function error(message, type) { function error(message, type) {
return [{message, type}]; return [{ message, type }];
} }
const MSG_NO_JS_QUERY_INTERFACE = ( const MSG_NO_JS_QUERY_INTERFACE =
"Please use ChromeUtils.generateQI rather than manually creating " + "Please use ChromeUtils.generateQI rather than manually creating " +
"JavaScript QueryInterface functions"); "JavaScript QueryInterface functions";
const MSG_NO_XPCOMUTILS_GENERATEQI = ( const MSG_NO_XPCOMUTILS_GENERATEQI =
"Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI"); "Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI";
/* globals nsIFlug */ /* globals nsIFlug */
function QueryInterface(iid) { function QueryInterface(iid) {
if (iid.equals(Ci.nsISupports) || if (
iid.equals(Ci.nsIMeh) || iid.equals(Ci.nsISupports) ||
iid.equals(nsIFlug) || iid.equals(Ci.nsIMeh) ||
iid.equals(Ci.amIFoo)) { iid.equals(nsIFlug) ||
iid.equals(Ci.amIFoo)
) {
return this; return this;
} }
throw Cr.NS_ERROR_NO_INTERFACE; throw Cr.NS_ERROR_NO_INTERFACE;
@ -63,7 +65,10 @@ ruleTester.run("use-chromeutils-generateqi", rule, {
errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property"), errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property"),
}, },
{ {
code: `X.prototype = { ${String(QueryInterface).replace(/^function /, "")} };`, code: `X.prototype = { ${String(QueryInterface).replace(
/^function /,
""
)} };`,
output: `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh", "nsIFlug", "amIFoo"]) };`, output: `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh", "nsIFlug", "amIFoo"]) };`,
errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property"), errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property"),
}, },
@ -74,4 +79,3 @@ ruleTester.run("use-chromeutils-generateqi", rule, {
}, },
], ],
}); });

View file

@ -17,12 +17,13 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
function callError(message) { function callError(message) {
return [{message, type: "CallExpression"}]; return [{ message, type: "CallExpression" }];
} }
const MESSAGE_IMPORT = "Please use ChromeUtils.import instead of Cu.import"; const MESSAGE_IMPORT = "Please use ChromeUtils.import instead of Cu.import";
const MESSAGE_DEFINE = ("Please use ChromeUtils.defineModuleGetter instead of " + const MESSAGE_DEFINE =
"XPCOMUtils.defineLazyModuleGetter"); "Please use ChromeUtils.defineModuleGetter instead of " +
"XPCOMUtils.defineLazyModuleGetter";
ruleTester.run("use-chromeutils-import", rule, { ruleTester.run("use-chromeutils-import", rule, {
valid: [ valid: [
@ -37,7 +38,7 @@ ruleTester.run("use-chromeutils-import", rule, {
"resource://gre/modules/Service.jsm", "resource://gre/modules/Service.jsm",
undefined, preServicesLambda);`, undefined, preServicesLambda);`,
{ {
options: [{allowCu: true}], options: [{ allowCu: true }],
code: `Cu.import("resource://gre/modules/Service.jsm");`, code: `Cu.import("resource://gre/modules/Service.jsm");`,
}, },
], ],
@ -63,7 +64,7 @@ ruleTester.run("use-chromeutils-import", rule, {
errors: callError(MESSAGE_IMPORT), errors: callError(MESSAGE_IMPORT),
}, },
{ {
options: [{allowCu: true}], options: [{ allowCu: true }],
code: `Components.utils.import("resource://gre/modules/Services.jsm", this);`, code: `Components.utils.import("resource://gre/modules/Services.jsm", this);`,
output: `ChromeUtils.import("resource://gre/modules/Services.jsm", this);`, output: `ChromeUtils.import("resource://gre/modules/Services.jsm", this);`,
errors: callError(MESSAGE_IMPORT), errors: callError(MESSAGE_IMPORT),
@ -77,4 +78,3 @@ ruleTester.run("use-chromeutils-import", rule, {
}, },
], ],
}); });

View file

@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code) { function invalidCode(code) {
let message = "provide a default value instead of using a try/catch block"; let message = "provide a default value instead of using a try/catch block";
return {code, errors: [{message, type: "TryStatement"}]}; return { code, errors: [{ message, type: "TryStatement" }] };
} }
let types = ["Bool", "Char", "Float", "Int"]; let types = ["Bool", "Char", "Float", "Int"];
@ -28,12 +28,15 @@ ruleTester.run("use-default-preference-values", rule, {
valid: [].concat( valid: [].concat(
methods.map(m => "blah = branch." + m + "('blah', true);"), methods.map(m => "blah = branch." + m + "('blah', true);"),
methods.map(m => "blah = branch." + m + "('blah');"), methods.map(m => "blah = branch." + m + "('blah');"),
methods.map(m => "try { canThrow();" + methods.map(
" blah = branch." + m + "('blah'); } catch(e) {}") m =>
"try { canThrow();" + " blah = branch." + m + "('blah'); } catch(e) {}"
)
), ),
invalid: [].concat( invalid: [].concat(
methods.map(m => methods.map(m =>
invalidCode("try { blah = branch." + m + "('blah'); } catch(e) {}")) invalidCode("try { blah = branch." + m + "('blah'); } catch(e) {}")
)
), ),
}); });

View file

@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code) { function invalidCode(code) {
let message = "use .includes instead of .indexOf"; let message = "use .includes instead of .indexOf";
return {code, errors: [{message, type: "BinaryExpression"}]}; return { code, errors: [{ message, type: "BinaryExpression" }] };
} }
ruleTester.run("use-includes-instead-of-indexOf", rule, { ruleTester.run("use-includes-instead-of-indexOf", rule, {

View file

@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code) { function invalidCode(code) {
let message = "use .ownerGlobal instead of .ownerDocument.defaultView"; let message = "use .ownerGlobal instead of .ownerDocument.defaultView";
return {code, errors: [{message, type: "MemberExpression"}]}; return { code, errors: [{ message, type: "MemberExpression" }] };
} }
ruleTester.run("use-ownerGlobal", rule, { ruleTester.run("use-ownerGlobal", rule, {
@ -29,8 +29,7 @@ ruleTester.run("use-ownerGlobal", rule, {
], ],
invalid: [ invalid: [
invalidCode("aEvent.target.ownerDocument.defaultView;"), invalidCode("aEvent.target.ownerDocument.defaultView;"),
invalidCode( invalidCode("this.DOMPointNode.ownerDocument.defaultView.getSelection();"),
"this.DOMPointNode.ownerDocument.defaultView.getSelection();"),
invalidCode("windowToMessageManager(node.ownerDocument.defaultView);"), invalidCode("windowToMessageManager(node.ownerDocument.defaultView);"),
], ],
}); });

View file

@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code, methodName) { function invalidCode(code, methodName) {
let message = `{Array/String}.${methodName} doesn't modify the instance in-place`; let message = `{Array/String}.${methodName} doesn't modify the instance in-place`;
return {code, errors: [{message, type: "ExpressionStatement"}]}; return { code, errors: [{ message, type: "ExpressionStatement" }] };
} }
ruleTester.run("use-returnValue", rule, { ruleTester.run("use-returnValue", rule, {

View file

@ -18,7 +18,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
function invalidCode(code, name) { function invalidCode(code, name) {
let message = `Use Services.${name} rather than getService().`; let message = `Use Services.${name} rather than getService().`;
return {code, errors: [{message, type: "CallExpression"}]}; return { code, errors: [{ message, type: "CallExpression" }] };
} }
ruleTester.run("use-services", rule, { ruleTester.run("use-services", rule, {
@ -28,10 +28,13 @@ ruleTester.run("use-services", rule, {
"Services.wm.addListener()", "Services.wm.addListener()",
], ],
invalid: [ invalid: [
invalidCode('Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);', invalidCode(
"wm"), 'Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);',
"wm"
),
invalidCode( invalidCode(
'Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(Components.interfaces.nsIAppStartup);', 'Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(Components.interfaces.nsIAppStartup);',
"startup"), "startup"
),
], ],
}); });

View file

@ -11,53 +11,73 @@ add_task(async function test_profile_feature_jsallocations() {
if (!AppConstants.MOZ_GECKO_PROFILER) { if (!AppConstants.MOZ_GECKO_PROFILER) {
return; return;
} }
Assert.ok(!Services.profiler.IsActive(), "The profiler is not currently active"); Assert.ok(
!Services.profiler.IsActive(),
"The profiler is not currently active"
);
startProfiler({ features: ["threads", "js", "jsallocations"] }); startProfiler({ features: ["threads", "js", "jsallocations"] });
const url = BASE_URL + "do_work_500ms.html"; const url = BASE_URL + "do_work_500ms.html";
await BrowserTestUtils.withNewTab(url, async (contentBrowser) => { await BrowserTestUtils.withNewTab(url, async contentBrowser => {
const contentPid = await ContentTask.spawn(contentBrowser, null, const contentPid = await ContentTask.spawn(
() => Services.appinfo.processID); contentBrowser,
null,
() => Services.appinfo.processID
);
// Wait 500ms so that the tab finishes executing. // Wait 500ms so that the tab finishes executing.
await wait(500); await wait(500);
// Check that we can get some allocations when the feature is turned on. // Check that we can get some allocations when the feature is turned on.
{ {
const { parentThread, contentThread } = await stopProfilerAndGetThreads(contentPid); const { parentThread, contentThread } = await stopProfilerAndGetThreads(
Assert.greater(getPayloadsOfType(parentThread, "JS allocation").length, 0, contentPid
"Allocations were recorded for the parent process' main thread when the " + );
"JS Allocation feature was turned on."); Assert.greater(
Assert.greater(getPayloadsOfType(contentThread, "JS allocation").length, 0, getPayloadsOfType(parentThread, "JS allocation").length,
"Allocations were recorded for the content process' main thread when the " + 0,
"JS Allocation feature was turned on."); "Allocations were recorded for the parent process' main thread when the " +
} "JS Allocation feature was turned on."
);
Assert.greater(
getPayloadsOfType(contentThread, "JS allocation").length,
0,
"Allocations were recorded for the content process' main thread when the " +
"JS Allocation feature was turned on."
);
}
// Flush out any straggling allocation markers that may have not been collected // Flush out any straggling allocation markers that may have not been collected
// yet by starting and stopping the profiler once. // yet by starting and stopping the profiler once.
startProfiler({ features: ["threads", "js"] }); startProfiler({ features: ["threads", "js"] });
await stopProfilerAndGetThreads(contentPid); await stopProfilerAndGetThreads(contentPid);
// Now reload the tab with a clean run. // Now reload the tab with a clean run.
gBrowser.reload(); gBrowser.reload();
await wait(500); await wait(500);
startProfiler({ features: ["threads", "js"] }); startProfiler({ features: ["threads", "js"] });
// Check that no allocations were recorded, and allocation tracking was correctly // Check that no allocations were recorded, and allocation tracking was correctly
// turned off. // turned off.
{ {
const { parentThread, contentThread } = await stopProfilerAndGetThreads(contentPid); const { parentThread, contentThread } = await stopProfilerAndGetThreads(
Assert.equal( contentPid
getPayloadsOfType(parentThread, "JS allocation").length, 0, );
"No allocations were recorded for the parent processes' main thread when " + Assert.equal(
"JS allocation was not turned on."); getPayloadsOfType(parentThread, "JS allocation").length,
0,
"No allocations were recorded for the parent processes' main thread when " +
"JS allocation was not turned on."
);
Assert.equal( Assert.equal(
getPayloadsOfType(contentThread, "JS allocation").length, 0, getPayloadsOfType(contentThread, "JS allocation").length,
"No allocations were recorded for the content processes' main thread when " + 0,
"JS allocation was not turned on."); "No allocations were recorded for the content processes' main thread when " +
} "JS allocation was not turned on."
);
}
}); });
}); });
@ -74,7 +94,7 @@ async function doAtLeastOnePeriodicSample() {
const sampleCount = await getProfileSampleCount(); const sampleCount = await getProfileSampleCount();
// Create an infinite loop until a sample has been collected. // Create an infinite loop until a sample has been collected.
while (true) { while (true) {
if (sampleCount < await getProfileSampleCount()) { if (sampleCount < (await getProfileSampleCount())) {
return; return;
} }
} }
@ -87,7 +107,9 @@ async function stopProfilerAndGetThreads(contentPid) {
Services.profiler.StopProfiler(); Services.profiler.StopProfiler();
const parentThread = profile.threads[0]; const parentThread = profile.threads[0];
const contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid); const contentProcess = profile.processes.find(
p => p.threads[0].pid == contentPid
);
if (!contentProcess) { if (!contentProcess) {
throw new Error("Could not find the content process."); throw new Error("Could not find the content process.");
} }

View file

@ -24,7 +24,9 @@ add_task(async function test_profile_single_frame_page_info() {
let pageFound = false; let pageFound = false;
// We need to find the correct content process for that tab. // We need to find the correct content process for that tab.
let contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid); let contentProcess = profile.processes.find(
p => p.threads[0].pid == contentPid
);
for (const page of contentProcess.pages) { for (const page of contentProcess.pages) {
if (page.url == url) { if (page.url == url) {
Assert.equal(page.url, url); Assert.equal(page.url, url);

View file

@ -24,7 +24,9 @@ add_task(async function test_profile_pushstate_page_info() {
let foundPage = 0; let foundPage = 0;
// We need to find the correct content process for that tab. // We need to find the correct content process for that tab.
let contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid); let contentProcess = profile.processes.find(
p => p.threads[0].pid == contentPid
);
for (const page of contentProcess.pages) { for (const page of contentProcess.pages) {
// Before pushState // Before pushState
if (page.url == url) { if (page.url == url) {

View file

@ -24,7 +24,9 @@ add_task(async function test_profile_replacestate_page_info() {
let foundPage = 0; let foundPage = 0;
// We need to find the correct content process for that tab. // We need to find the correct content process for that tab.
let contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid); let contentProcess = profile.processes.find(
p => p.threads[0].pid == contentPid
);
for (const page of contentProcess.pages) { for (const page of contentProcess.pages) {
// Before replaceState // Before replaceState
if (page.url == url) { if (page.url == url) {

View file

@ -25,7 +25,9 @@ add_task(async function test_profile_multi_frame_page_info() {
let foundPage = 0; let foundPage = 0;
// We need to find the correct content process for that tab. // We need to find the correct content process for that tab.
let contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid); let contentProcess = profile.processes.find(
p => p.threads[0].pid == contentPid
);
for (const page of contentProcess.pages) { for (const page of contentProcess.pages) {
// Parent page // Parent page
if (page.url == url) { if (page.url == url) {

View file

@ -1,5 +1,6 @@
const { BrowserTestUtils } = ChromeUtils.import(
const { BrowserTestUtils } = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm"); "resource://testing-common/BrowserTestUtils.jsm"
);
const BASE_URL = "http://example.com/browser/tools/profiler/tests/browser/"; const BASE_URL = "http://example.com/browser/tools/profiler/tests/browser/";
@ -44,7 +45,7 @@ function wait(time) {
* @return {Array} The payloads. * @return {Array} The payloads.
*/ */
function getPayloadsOfType(thread, type) { function getPayloadsOfType(thread, type) {
const {markers} = thread; const { markers } = thread;
const results = []; const results = [];
for (const markerTuple of markers.data) { for (const markerTuple of markers.data) {
const payload = markerTuple[markers.schema.data]; const payload = markerTuple[markers.schema.data];

View file

@ -1,59 +1,63 @@
"use strict"; "use strict";
(function() { (function() {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { Services } = ChromeUtils.import(
"resource://gre/modules/Services.jsm"
function startProfiler(settings) {
Services.profiler.StartProfiler(
settings.entries,
settings.interval,
settings.features,
settings.threads,
settings.duration
); );
info("Profiler has started"); function startProfiler(settings) {
} Services.profiler.StartProfiler(
settings.entries,
settings.interval,
settings.features,
settings.threads,
settings.duration
);
function getProfile() { info("Profiler has started");
const profile = Services.profiler.getProfileData();
info("We got a profile, run the mochitest with `--keep-open true` to see the logged profile in the Web Console.");
// Run the mochitest with `--keep-open true` to see the logged profile in the
// Web console.
console.log(profile);
return profile;
}
function stopProfiler() {
Services.profiler.StopProfiler();
info("Profiler has stopped");
}
function end(error) {
if (error) {
ok(false, `We got an error: ${error}`);
} else {
ok(true, "We ran the whole process");
} }
SimpleTest.finish();
}
async function runTest(settings, workload) { function getProfile() {
SimpleTest.waitForExplicitFinish(); const profile = Services.profiler.getProfileData();
try { info(
await startProfiler(settings); "We got a profile, run the mochitest with `--keep-open true` to see the logged profile in the Web Console."
await workload(); );
await getProfile();
await stopProfiler(); // Run the mochitest with `--keep-open true` to see the logged profile in the
await end(); // Web console.
} catch (e) { console.log(profile);
// By catching and handling the error, we're being nice to mochitest
// runners: instead of waiting for the timeout, we fail right away. return profile;
await end(e);
} }
}
window.runTest = runTest; function stopProfiler() {
Services.profiler.StopProfiler();
info("Profiler has stopped");
}
function end(error) {
if (error) {
ok(false, `We got an error: ${error}`);
} else {
ok(true, "We ran the whole process");
}
SimpleTest.finish();
}
async function runTest(settings, workload) {
SimpleTest.waitForExplicitFinish();
try {
await startProfiler(settings);
await workload();
await getProfile();
await stopProfiler();
await end();
} catch (e) {
// By catching and handling the error, we're being nice to mochitest
// runners: instead of waiting for the timeout, we fail right away.
await end(e);
}
}
window.runTest = runTest;
})(); })();

View file

@ -1,10 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); var { AppConstants } = ChromeUtils.import(
var {setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm"); "resource://gre/modules/AppConstants.jsm"
);
var { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
/** /**
* Get the payloads of a type recursively, including from all subprocesses. * Get the payloads of a type recursively, including from all subprocesses.
@ -15,7 +17,7 @@ var {setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
* @return {Array} The final payloads. * @return {Array} The final payloads.
*/ */
function getAllPayloadsOfType(profile, type, payloadTarget = []) { function getAllPayloadsOfType(profile, type, payloadTarget = []) {
for (const {markers} of profile.threads) { for (const { markers } of profile.threads) {
for (const markerTuple of markers.data) { for (const markerTuple of markers.data) {
const payload = markerTuple[markers.schema.data]; const payload = markerTuple[markers.schema.data];
if (payload && payload.type === type) { if (payload && payload.type === type) {
@ -31,7 +33,6 @@ function getAllPayloadsOfType(profile, type, payloadTarget = []) {
return payloadTarget; return payloadTarget;
} }
/** /**
* This is a helper function be able to run `await wait(500)`. Unfortunately this * This is a helper function be able to run `await wait(500)`. Unfortunately this
* is needed as the act of collecting functions relies on the periodic sampling of * is needed as the act of collecting functions relies on the periodic sampling of

View file

@ -1,77 +1,81 @@
// Check that asm.js code shows up on the stack. // Check that asm.js code shows up on the stack.
function run_test() { function run_test() {
// Just skip the test if the profiler component isn't present. // Just skip the test if the profiler component isn't present.
if (!AppConstants.MOZ_GECKO_PROFILER) { if (!AppConstants.MOZ_GECKO_PROFILER) {
return; return;
} }
// This test assumes that it's starting on an empty profiler stack. // This test assumes that it's starting on an empty profiler stack.
// (Note that the other profiler tests also assume the profiler // (Note that the other profiler tests also assume the profiler
// isn't already started.) // isn't already started.)
Assert.ok(!Services.profiler.IsActive()); Assert.ok(!Services.profiler.IsActive());
let jsFuns = Cu.getJSTestingFunctions(); let jsFuns = Cu.getJSTestingFunctions();
if (!jsFuns.isAsmJSCompilationAvailable()) if (!jsFuns.isAsmJSCompilationAvailable()) {
return;
}
const ms = 10;
Services.profiler.StartProfiler(10000, ms, ["js"]);
let stack = null;
function ffi_function() {
var delayMS = 5;
while (1) {
let then = Date.now();
do {
// do nothing
} while (Date.now() - then < delayMS);
var thread0 = Services.profiler.getProfileData().threads[0];
if (delayMS > 30000) {
return; return;
}
const ms = 10; delayMS *= 2;
Services.profiler.StartProfiler(10000, ms, ["js"]);
let stack = null; if (thread0.samples.data.length == 0) {
function ffi_function() { continue;
var delayMS = 5; }
while (1) {
let then = Date.now();
do {
// do nothing
} while (Date.now() - then < delayMS);
var thread0 = Services.profiler.getProfileData().threads[0]; var lastSample = thread0.samples.data[thread0.samples.data.length - 1];
stack = String(getInflatedStackLocations(thread0, lastSample));
if (delayMS > 30000) if (stack.includes("trampoline")) {
return; return;
}
delayMS *= 2;
if (thread0.samples.data.length == 0)
continue;
var lastSample = thread0.samples.data[thread0.samples.data.length - 1];
stack = String(getInflatedStackLocations(thread0, lastSample));
if (stack.includes("trampoline"))
return;
}
} }
}
function asmjs_module(global, ffis) { function asmjs_module(global, ffis) {
"use asm"; "use asm";
var ffi = ffis.ffi; var ffi = ffis.ffi;
function asmjs_function() { function asmjs_function() {
ffi(); ffi();
}
return asmjs_function;
} }
return asmjs_function;
}
Assert.ok(jsFuns.isAsmJSModule(asmjs_module)); Assert.ok(jsFuns.isAsmJSModule(asmjs_module));
var asmjs_function = asmjs_module(null, {ffi: ffi_function}); var asmjs_function = asmjs_module(null, { ffi: ffi_function });
Assert.ok(jsFuns.isAsmJSFunction(asmjs_function)); Assert.ok(jsFuns.isAsmJSFunction(asmjs_function));
asmjs_function(); asmjs_function();
Assert.notEqual(stack, null); Assert.notEqual(stack, null);
var i1 = stack.indexOf("entry trampoline"); var i1 = stack.indexOf("entry trampoline");
Assert.ok(i1 !== -1); Assert.ok(i1 !== -1);
var i2 = stack.indexOf("asmjs_function"); var i2 = stack.indexOf("asmjs_function");
Assert.ok(i2 !== -1); Assert.ok(i2 !== -1);
var i3 = stack.indexOf("exit trampoline"); var i3 = stack.indexOf("exit trampoline");
Assert.ok(i3 !== -1); Assert.ok(i3 !== -1);
var i4 = stack.indexOf("ffi_function"); var i4 = stack.indexOf("ffi_function");
Assert.ok(i4 !== -1); Assert.ok(i4 !== -1);
Assert.ok(i1 < i2); Assert.ok(i1 < i2);
Assert.ok(i2 < i3); Assert.ok(i2 < i3);
Assert.ok(i3 < i4); Assert.ok(i3 < i4);
Services.profiler.StopProfiler(); Services.profiler.StopProfiler();
} }

View file

@ -2,51 +2,54 @@
// usable by a native unwinder to resume unwinding after encountering // usable by a native unwinder to resume unwinding after encountering
// JIT code, is pushed as expected. // JIT code, is pushed as expected.
function run_test() { function run_test() {
if (!AppConstants.MOZ_GECKO_PROFILER) { if (!AppConstants.MOZ_GECKO_PROFILER) {
return; return;
} }
// This test assumes that it's starting on an empty profiler stack. // This test assumes that it's starting on an empty profiler stack.
// (Note that the other profiler tests also assume the profiler // (Note that the other profiler tests also assume the profiler
// isn't already started.) // isn't already started.)
Assert.ok(!Services.profiler.IsActive()); Assert.ok(!Services.profiler.IsActive());
const ms = 5; const ms = 5;
Services.profiler.StartProfiler(10000, ms, ["js"]); Services.profiler.StartProfiler(10000, ms, ["js"]);
function has_arbitrary_name_in_stack() { function has_arbitrary_name_in_stack() {
// A frame for |arbitrary_name| has been pushed. Do a sequence of // A frame for |arbitrary_name| has been pushed. Do a sequence of
// increasingly long spins until we get a sample. // increasingly long spins until we get a sample.
var delayMS = 5; var delayMS = 5;
while (1) { while (1) {
info("loop: ms = " + delayMS); info("loop: ms = " + delayMS);
const then = Date.now(); const then = Date.now();
do { do {
let n = 10000; let n = 10000;
while (--n); // OSR happens here while (--n) {} // OSR happens here
// Spin in the hope of getting a sample. // Spin in the hope of getting a sample.
} while (Date.now() - then < delayMS); } while (Date.now() - then < delayMS);
let profile = Services.profiler.getProfileData().threads[0]; let profile = Services.profiler.getProfileData().threads[0];
// Go through all of the stacks, and search for this function name. // Go through all of the stacks, and search for this function name.
for (const sample of profile.samples.data) { for (const sample of profile.samples.data) {
const stack = getInflatedStackLocations(profile, sample); const stack = getInflatedStackLocations(profile, sample);
info(`The following stack was found: ${stack}`); info(`The following stack was found: ${stack}`);
for (var i = 0; i < stack.length; i++) { for (var i = 0; i < stack.length; i++) {
if (stack[i].match(/arbitrary_name/)) { if (stack[i].match(/arbitrary_name/)) {
// This JS sample was correctly found. // This JS sample was correctly found.
return true; return true;
} }
}
}
// Continue running this function with an increasingly long delay.
delayMS *= 2;
if (delayMS > 30000) {
return false;
}
} }
}
// Continue running this function with an increasingly long delay.
delayMS *= 2;
if (delayMS > 30000) {
return false;
}
} }
Assert.ok(has_arbitrary_name_in_stack(), "A JS frame was found before the test timeout."); }
Services.profiler.StopProfiler(); Assert.ok(
has_arbitrary_name_in_stack(),
"A JS frame was found before the test timeout."
);
Services.profiler.StopProfiler();
} }

View file

@ -11,7 +11,7 @@ function run_test() {
(function() { (function() {
Services.profiler.StopProfiler(); Services.profiler.StopProfiler();
let n = 10000; let n = 10000;
while (--n); // OSR happens here with the profiler disabled. while (--n) {} // OSR happens here with the profiler disabled.
// An assertion will fail when this function returns, if the // An assertion will fail when this function returns, if the
// profiler stack was misbalanced. // profiler stack was misbalanced.
})(); })();

View file

@ -9,7 +9,7 @@ function run_test() {
(function() { (function() {
Services.profiler.StartProfiler(100, 10, ["js"]); Services.profiler.StartProfiler(100, 10, ["js"]);
let n = 10000; let n = 10000;
while (--n); // OSR happens here with the profiler enabled. while (--n) {} // OSR happens here with the profiler enabled.
// An assertion will fail when this function returns, if the // An assertion will fail when this function returns, if the
// profiler stack was misbalanced. // profiler stack was misbalanced.
})(); })();

View file

@ -2,7 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); const { FileUtils } = ChromeUtils.import(
"resource://gre/modules/FileUtils.jsm"
);
/** /**
* Test that the IOInterposer is working correctly to capture main thread IO. * Test that the IOInterposer is working correctly to capture main thread IO.
@ -21,10 +23,16 @@ add_task(async () => {
{ {
const filename = "profiler-mainthreadio-test-firstrun"; const filename = "profiler-mainthreadio-test-firstrun";
const payloads = await startProfilerAndgetFileIOPayloads(["mainthreadio"], filename); const payloads = await startProfilerAndgetFileIOPayloads(
["mainthreadio"],
filename
);
greater(payloads.length, 0, greater(
"FileIO markers were found when using the mainthreadio feature on the profiler."); payloads.length,
0,
"FileIO markers were found when using the mainthreadio feature on the profiler."
);
// It would be better to check on the filename, but Linux does not currently include // It would be better to check on the filename, but Linux does not currently include
// it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1533531 // it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1533531
@ -36,18 +44,27 @@ add_task(async () => {
const filename = "profiler-mainthreadio-test-no-instrumentation"; const filename = "profiler-mainthreadio-test-no-instrumentation";
const payloads = await startProfilerAndgetFileIOPayloads([], filename); const payloads = await startProfilerAndgetFileIOPayloads([], filename);
equal(payloads.length, 0, equal(
"No FileIO markers are found when the mainthreadio feature is not turned on " + payloads.length,
"in the profiler."); 0,
"No FileIO markers are found when the mainthreadio feature is not turned on " +
"in the profiler."
);
} }
{ {
const filename = "profiler-mainthreadio-test-secondrun"; const filename = "profiler-mainthreadio-test-secondrun";
const payloads = await startProfilerAndgetFileIOPayloads(["mainthreadio"], filename); const payloads = await startProfilerAndgetFileIOPayloads(
["mainthreadio"],
filename
);
greater(payloads.length, 0, greater(
"FileIO markers were found when re-starting the mainthreadio feature on the " + payloads.length,
"profiler."); 0,
"FileIO markers were found when re-starting the mainthreadio feature on the " +
"profiler."
);
// It would be better to check on the filename, but Linux does not currently include // It would be better to check on the filename, but Linux does not currently include
// it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1533531 // it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1533531
// ok(hasWritePayload(payloads, filename), // ok(hasWritePayload(payloads, filename),
@ -67,14 +84,13 @@ async function startProfilerAndgetFileIOPayloads(features, filename) {
const threads = []; const threads = [];
Services.profiler.StartProfiler(entries, interval, features, threads); Services.profiler.StartProfiler(entries, interval, features, threads);
const file = FileUtils.getFile("TmpD", [filename]); const file = FileUtils.getFile("TmpD", [filename]);
if (file.exists()) { if (file.exists()) {
console.warn( console.warn(
"This test is triggering FileIO by writing to a file. However, the test found an " + "This test is triggering FileIO by writing to a file. However, the test found an " +
"existing file at the location it was trying to write to. This could happen " + "existing file at the location it was trying to write to. This could happen " +
"because a previous run of the test failed to clean up after itself. This test " + "because a previous run of the test failed to clean up after itself. This test " +
" will now clean up that file before running the test again." " will now clean up that file before running the test again."
); );
file.remove(false); file.remove(false);
} }

View file

@ -7,7 +7,11 @@
/* eslint-env webextensions */ /* eslint-env webextensions */
const Quitter = { const Quitter = {
quit() { browser.runtime.sendMessage("quit"); }, quit() {
browser.runtime.sendMessage("quit");
},
}; };
window.wrappedJSObject.Quitter = cloneInto(Quitter, window, {cloneFunctions: true}); window.wrappedJSObject.Quitter = cloneInto(Quitter, window, {
cloneFunctions: true,
});

View file

@ -2,14 +2,16 @@
/* globals ExtensionAPI */ /* globals ExtensionAPI */
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
this.quitter = class extends ExtensionAPI { this.quitter = class extends ExtensionAPI {
getAPI(context) { getAPI(context) {
return { return {
quitter: { quitter: {
async quit() { async quit() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); let browserWindow = Services.wm.getMostRecentWindow(
"navigator:browser"
);
if (browserWindow && browserWindow.gBrowserInit) { if (browserWindow && browserWindow.gBrowserInit) {
await browserWindow.gBrowserInit.idleTasksFinishedPromise; await browserWindow.gBrowserInit.idleTasksFinishedPromise;
} }

View file

@ -19,15 +19,17 @@ var apply = () => {
$(".filter:checked").each(function(index) { $(".filter:checked").each(function(index) {
for (let kind of this.name.split(",")) { for (let kind of this.name.split(",")) {
if (!(kinds.includes(kind))) if (!kinds.includes(kind)) {
kinds.push(kind); kinds.push(kind);
}
} }
// Checkbox element values are generated by Section.get_context() in app.py // Checkbox element values are generated by Section.get_context() in app.py
let attrs = JSON.parse(this.value); let attrs = JSON.parse(this.value);
for (let attr in attrs) { for (let attr in attrs) {
if (!(attr in filters)) if (!(attr in filters)) {
filters[attr] = []; filters[attr] = [];
}
let values = attrs[attr]; let values = attrs[attr];
filters[attr] = filters[attr].concat(values); filters[attr] = filters[attr].concat(values);
@ -35,24 +37,29 @@ var apply = () => {
}); });
updateLabels(); updateLabels();
if (Object.keys(filters).length == 0 || (Object.keys(filters).length == 1 && "build_type" in filters)) { if (
Object.keys(filters).length == 0 ||
(Object.keys(filters).length == 1 && "build_type" in filters)
) {
selection.value = ""; selection.value = "";
count.innerHTML = "0 tasks selected"; count.innerHTML = "0 tasks selected";
return; return;
} }
var taskMatches = (label) => { var taskMatches = label => {
let task = tasks[label]; let task = tasks[label];
// If no box for the given kind has been checked, this task is // If no box for the given kind has been checked, this task is
// automatically not selected. // automatically not selected.
if (!(kinds.includes(task.kind))) if (!kinds.includes(task.kind)) {
return false; return false;
}
for (let attr in filters) { for (let attr in filters) {
let values = filters[attr]; let values = filters[attr];
if (!(attr in task) || values.includes(task[attr])) if (!(attr in task) || values.includes(task[attr])) {
continue; continue;
}
return false; return false;
} }
return true; return true;

View file

@ -4,8 +4,9 @@ var lastChecked = {};
// implements shift+click // implements shift+click
labels.click(function(e) { labels.click(function(e) {
if (e.target.tagName === "INPUT") if (e.target.tagName === "INPUT") {
return; return;
}
let box = $("#" + this.htmlFor)[0]; let box = $("#" + this.htmlFor)[0];
let activeSection = $("div.tab-pane.active")[0].id; let activeSection = $("div.tab-pane.active")[0].id;
@ -15,13 +16,16 @@ labels.click(function(e) {
let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
if (e.shiftKey) { if (e.shiftKey) {
if (isFirefox) if (isFirefox) {
box.checked = !box.checked; box.checked = !box.checked;
}
let start = boxes.index(box); let start = boxes.index(box);
let end = boxes.index(lastChecked[activeSection]); let end = boxes.index(lastChecked[activeSection]);
boxes.slice(Math.min(start, end), Math.max(start, end) + 1).prop("checked", box.checked); boxes
.slice(Math.min(start, end), Math.max(start, end) + 1)
.prop("checked", box.checked);
apply(); apply();
} }
} }