diff --git a/devtools/.eslintrc.js b/devtools/.eslintrc.js index cad32749514a..f024c4229b67 100644 --- a/devtools/.eslintrc.js +++ b/devtools/.eslintrc.js @@ -27,6 +27,7 @@ module.exports = { // subdirectories of this directory. "mozilla/reject-some-requires": ["error", "^devtools/shared/platform/(chome|content)/"], "mozilla/var-only-at-top-level": "error", + "mozilla/use-chromeutils-import": ["error", {allowCu: true}], // Rules from the React plugin "react/display-name": "error", diff --git a/tools/lint/docs/linters/eslint-plugin-mozilla.rst b/tools/lint/docs/linters/eslint-plugin-mozilla.rst index dff52faff1e2..4f5ac9f33fab 100644 --- a/tools/lint/docs/linters/eslint-plugin-mozilla.rst +++ b/tools/lint/docs/linters/eslint-plugin-mozilla.rst @@ -256,6 +256,13 @@ object is assigned to another variable e.g.: var b = gBrowser; b.content // Would not be detected as a CPOW. +use-chromeutils-import +---------------------- + +Require use of ``ChromeUtils.import`` and ``ChromeUtils.defineModuleGetter`` +rather than ``Components.utils.import`` and +``XPCOMUtils.defineLazyModuleGetter``. + use-default-preference-values --------------- diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js index ebc2e9052906..239787f21de9 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js @@ -169,6 +169,7 @@ module.exports = { "mozilla/no-import-into-var-and-global": "error", "mozilla/no-useless-parameters": "error", "mozilla/no-useless-removeEventListener": "error", + "mozilla/use-chromeutils-import": "error", "mozilla/use-default-preference-values": "error", "mozilla/use-ownerGlobal": "error", "mozilla/use-services": "error", diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js index 760ff386912c..5df1bbba3463 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js @@ -23,6 +23,7 @@ const callExpressionDefinitions = [ /^loader\.lazyRequireGetter\(this, "(\w+)"/, /^XPCOMUtils\.defineLazyGetter\(this, "(\w+)"/, /^XPCOMUtils\.defineLazyModuleGetter\(this, "(\w+)"/, + /^ChromeUtils\.defineModuleGetter\(this, "(\w+)"/, /^XPCOMUtils\.defineLazyPreferenceGetter\(this, "(\w+)"/, /^XPCOMUtils\.defineLazyScriptGetter\(this, "(\w+)"/, /^XPCOMUtils\.defineLazyServiceGetter\(this, "(\w+)"/, diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js index 7fdcc27594d9..a9ca21855bbc 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js @@ -57,6 +57,7 @@ module.exports = { "reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"), "reject-some-requires": require("../lib/rules/reject-some-requires"), + "use-chromeutils-import": require("../lib/rules/use-chromeutils-import"), "use-default-preference-values": require("../lib/rules/use-default-preference-values"), "use-ownerGlobal": require("../lib/rules/use-ownerGlobal"), @@ -83,6 +84,7 @@ module.exports = { "no-useless-removeEventListener": "off", "reject-importGlobalProperties": "off", "reject-some-requires": "off", + "use-chromeutils-import": "off", "use-default-preference-values": "off", "use-ownerGlobal": "off", "use-services": "off", diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-import.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-import.js new file mode 100644 index 000000000000..495de071d64f --- /dev/null +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-import.js @@ -0,0 +1,79 @@ +/** + * @fileoverview Reject use of Cu.import and XPCOMUtils.defineLazyModuleGetter + * in favor of ChromeUtils. + * + * 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +"use strict"; + +// ----------------------------------------------------------------------------- +// Rule Definition +// ----------------------------------------------------------------------------- + +function isIdentifier(node, id) { + return node && node.type === "Identifier" && node.name === id; +} + +function isMemberExpression(node, object, member) { + return (node.type === "MemberExpression" && + isIdentifier(node.object, object) && + isIdentifier(node.property, member)); +} + +module.exports = { + meta: { + schema: [ + { + "type": "object", + "properties": { + "allowCu": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + fixable: "code" + }, + + create(context) { + return { + "CallExpression": function(node) { + if (node.callee.type !== "MemberExpression") { + return; + } + + let {allowCu} = context.options[0] || {}; + let {callee} = node; + + // Is the expression starting with `Cu` or `Components.utils`? + if (((!allowCu && isIdentifier(callee.object, "Cu")) || + isMemberExpression(callee.object, "Components", "utils")) && + isIdentifier(callee.property, "import")) { + context.report({ + node, + message: "Please use ChromeUtils.import instead of Cu.import", + fix(fixer) { + return fixer.replaceText(callee, "ChromeUtils.import"); + } + }); + } + + if (isMemberExpression(callee, "XPCOMUtils", "defineLazyModuleGetter") && + node.arguments.length < 4) { + context.report({ + node, + message: ("Please use ChromeUtils.defineModuleGetter instead of " + + "XPCOMUtils.defineLazyModuleGetter"), + fix(fixer) { + return fixer.replaceText(callee, "ChromeUtils.defineModuleGetter"); + } + }); + } + } + }; + } +}; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/tests/use-chromeutils-import.js b/tools/lint/eslint/eslint-plugin-mozilla/tests/use-chromeutils-import.js new file mode 100644 index 000000000000..66840066ac48 --- /dev/null +++ b/tools/lint/eslint/eslint-plugin-mozilla/tests/use-chromeutils-import.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require("../lib/rules/use-chromeutils-import"); +var RuleTester = require("eslint/lib/testers/rule-tester"); + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +function callError(message) { + return [{message, type: "CallExpression"}]; +} + +const MESSAGE_IMPORT = "Please use ChromeUtils.import instead of Cu.import"; +const MESSAGE_DEFINE = ("Please use ChromeUtils.defineModuleGetter instead of " + + "XPCOMUtils.defineLazyModuleGetter"); + +ruleTester.run("use-chromeutils-import", rule, { + valid: [ + `ChromeUtils.import("resource://gre/modules/Service.jsm");`, + `ChromeUtils.import("resource://gre/modules/Service.jsm", this);`, + `ChromeUtils.defineModuleGetter(this, "Services", + "resource://gre/modules/Service.jsm");`, + `XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Service.jsm", + "Foo");`, + `XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Service.jsm", + undefined, preServicesLambda);`, + { + options: [{allowCu: true}], + code: `Cu.import("resource://gre/modules/Service.jsm");` + } + ], + invalid: [ + { + code: `Cu.import("resource://gre/modules/Services.jsm");`, + output: `ChromeUtils.import("resource://gre/modules/Services.jsm");`, + errors: callError(MESSAGE_IMPORT) + }, + { + code: `Cu.import("resource://gre/modules/Services.jsm", this);`, + output: `ChromeUtils.import("resource://gre/modules/Services.jsm", this);`, + errors: callError(MESSAGE_IMPORT) + }, + { + code: `Components.utils.import("resource://gre/modules/Services.jsm");`, + output: `ChromeUtils.import("resource://gre/modules/Services.jsm");`, + errors: callError(MESSAGE_IMPORT) + }, + { + code: `Components.utils.import("resource://gre/modules/Services.jsm");`, + output: `ChromeUtils.import("resource://gre/modules/Services.jsm");`, + errors: callError(MESSAGE_IMPORT) + }, + { + options: [{allowCu: true}], + code: `Components.utils.import("resource://gre/modules/Services.jsm", this);`, + output: `ChromeUtils.import("resource://gre/modules/Services.jsm", this);`, + errors: callError(MESSAGE_IMPORT) + }, + { + code: `XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm");`, + output: `ChromeUtils.defineModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm");`, + errors: callError(MESSAGE_DEFINE) + } + ] +}); +