import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,363 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
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/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

View file

@ -0,0 +1,188 @@
/**
* @fileoverview functions for scanning an AST for globals including
* traversing referenced scripts.
* 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";
const path = require("path");
const fs = require("fs");
const helpers = require("./helpers");
const escope = require("escope");
const estraverse = require("estraverse");
/**
* Parses a list of "name:boolean_value" or/and "name" options divided by comma or
* whitespace.
*
* This function was copied from eslint.js
*
* @param {string} string The string to parse.
* @param {Comment} comment The comment node which has the string.
* @returns {Object} Result map object of names and boolean values
*/
function parseBooleanConfig(string, comment) {
let items = {};
// Collapse whitespace around : to make parsing easier
string = string.replace(/\s*:\s*/g, ":");
// Collapse whitespace around ,
string = string.replace(/\s*,\s*/g, ",");
string.split(/\s|,+/).forEach(function(name) {
if (!name) {
return;
}
let pos = name.indexOf(":");
let value = undefined;
if (pos !== -1) {
value = name.substring(pos + 1, name.length);
name = name.substring(0, pos);
}
items[name] = {
value: (value === "true"),
comment: comment
};
});
return items;
}
/**
* Global discovery can require parsing many files. This map of
* {String} => {Object} caches what globals were discovered for a file path.
*/
const globalCache = new Map();
/**
* An object that returns found globals for given AST node types. Each prototype
* property should be named for a node type and accepts a node parameter and a
* parents parameter which is a list of the parent nodes of the current node.
* Each returns an array of globals found.
*
* @param {String} path
* The absolute path of the file being parsed.
*/
function GlobalsForNode(path) {
this.path = path;
this.root = helpers.getRootDir(path);
}
GlobalsForNode.prototype = {
BlockComment(node, parents) {
let value = node.value.trim();
let match = /^import-globals-from\s+(.+)$/.exec(value);
if (!match) {
return [];
}
let filePath = match[1].trim();
if (!path.isAbsolute(filePath)) {
let dirName = path.dirname(this.path);
filePath = path.resolve(dirName, filePath);
}
return module.exports.getGlobalsForFile(filePath);
},
ExpressionStatement(node, parents) {
let isGlobal = helpers.getIsGlobalScope(parents);
let names = helpers.convertExpressionToGlobals(node, isGlobal, this.root);
return names.map(name => { return { name, writable: true }});
},
};
module.exports = {
/**
* Returns all globals for a given file. Recursively searches through
* import-globals-from directives and also includes globals defined by
* standard eslint directives.
*
* @param {String} path
* The absolute path of the file to be parsed.
*/
getGlobalsForFile(path) {
if (globalCache.has(path)) {
return globalCache.get(path);
}
let content = fs.readFileSync(path, "utf8");
// Parse the content into an AST
let ast = helpers.getAST(content);
// Discover global declarations
let scopeManager = escope.analyze(ast);
let globalScope = scopeManager.acquire(ast);
let globals = Object.keys(globalScope.variables).map(v => ({
name: globalScope.variables[v].name,
writable: true,
}));
// Walk over the AST to find any of our custom globals
let handler = new GlobalsForNode(path);
helpers.walkAST(ast, (type, node, parents) => {
// We have to discover any globals that ESLint would have defined through
// comment directives
if (type == "BlockComment") {
let value = node.value.trim();
let match = /^globals?\s+(.+)$/.exec(value);
if (match) {
let values = parseBooleanConfig(match[1].trim(), node);
for (let name of Object.keys(values)) {
globals.push({
name,
writable: values[name].value
})
}
}
}
if (type in handler) {
let newGlobals = handler[type](node, parents);
globals.push.apply(globals, newGlobals);
}
});
globalCache.set(path, globals);
return globals;
},
/**
* Intended to be used as-is for an ESLint rule that parses for globals in
* the current file and recurses through import-globals-from directives.
*
* @param {Object} context
* The ESLint parsing context.
*/
getESLintGlobalParser(context) {
let globalScope;
let parser = {
Program(node) {
globalScope = context.getScope();
}
};
// Install thin wrappers around GlobalsForNode
let handler = new GlobalsForNode(helpers.getAbsoluteFilePath(context));
for (let type of Object.keys(GlobalsForNode.prototype)) {
parser[type] = function(node) {
let globals = handler[type](node, context.getAncestors());
helpers.addGlobals(globals, globalScope);
}
}
return parser;
}
};

View file

@ -0,0 +1,524 @@
/**
* @fileoverview A collection of helper functions.
* 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";
var escope = require("escope");
var espree = require("espree");
var estraverse = require("estraverse");
var path = require("path");
var fs = require("fs");
var ini = require("ini-parser");
var modules = null;
var directoryManifests = new Map();
var definitions = [
/^loader\.lazyGetter\(this, "(\w+)"/,
/^loader\.lazyImporter\(this, "(\w+)"/,
/^loader\.lazyServiceGetter\(this, "(\w+)"/,
/^loader\.lazyRequireGetter\(this, "(\w+)"/,
/^XPCOMUtils\.defineLazyGetter\(this, "(\w+)"/,
/^XPCOMUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
/^XPCOMUtils\.defineLazyServiceGetter\(this, "(\w+)"/,
/^XPCOMUtils\.defineConstant\(this, "(\w+)"/,
/^DevToolsUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
/^DevToolsUtils\.defineLazyGetter\(this, "(\w+)"/,
/^Object\.defineProperty\(this, "(\w+)"/,
/^Reflect\.defineProperty\(this, "(\w+)"/,
/^this\.__defineGetter__\("(\w+)"/,
/^this\.(\w+) =/
];
var imports = [
/^(?:Cu|Components\.utils)\.import\(".*\/((.*?)\.jsm?)"(?:, this)?\)/,
];
module.exports = {
/**
* Gets the abstract syntax tree (AST) of the JavaScript source code contained
* in sourceText.
*
* @param {String} sourceText
* Text containing valid JavaScript.
*
* @return {Object}
* The resulting AST.
*/
getAST: function(sourceText) {
// Use a permissive config file to allow parsing of anything that Espree
// can parse.
var config = this.getPermissiveConfig();
return espree.parse(sourceText, config);
},
/**
* A simplistic conversion of some AST nodes to a standard string form.
*
* @param {Object} node
* The AST node to convert.
*
* @return {String}
* The JS source for the node.
*/
getASTSource: function(node) {
switch (node.type) {
case "MemberExpression":
if (node.computed)
throw new Error("getASTSource unsupported computed MemberExpression");
return this.getASTSource(node.object) + "." + this.getASTSource(node.property);
case "ThisExpression":
return "this";
case "Identifier":
return node.name;
case "Literal":
return JSON.stringify(node.value);
case "CallExpression":
var args = node.arguments.map(a => this.getASTSource(a)).join(", ");
return this.getASTSource(node.callee) + "(" + args + ")";
case "ObjectExpression":
return "{}";
case "ExpressionStatement":
return this.getASTSource(node.expression) + ";";
case "FunctionExpression":
return "function() {}";
case "ArrowFunctionExpression":
return "() => {}";
case "AssignmentExpression":
return this.getASTSource(node.left) + " = " + this.getASTSource(node.right);
default:
throw new Error("getASTSource unsupported node type: " + node.type);
}
},
/**
* This walks an AST in a manner similar to ESLint passing node and comment
* events to the listener. The listener is expected to be a simple function
* which accepts node type, node and parents arguments.
*
* @param {Object} ast
* The AST to walk.
* @param {Function} listener
* A callback function to call for the nodes. Passed three arguments,
* event type, node and an array of parent nodes for the current node.
*/
walkAST(ast, listener) {
let parents = [];
let seenComments = new Set();
function sendCommentEvents(comments) {
if (!comments) {
return;
}
for (let comment of comments) {
if (seenComments.has(comment)) {
return;
}
seenComments.add(comment);
listener(comment.type + "Comment", comment, parents);
}
}
estraverse.traverse(ast, {
enter(node, parent) {
// Comments are held in node.comments for empty programs
let leadingComments = node.leadingComments;
if (node.type === "Program" && node.body.length == 0) {
leadingComments = node.comments;
}
sendCommentEvents(leadingComments);
listener(node.type, node, parents);
sendCommentEvents(node.trailingComments);
parents.push(node);
},
leave(node, parent) {
// TODO send comment exit events
listener(node.type + ":exit", node, parents);
if (parents.length == 0) {
throw new Error("Left more nodes than entered.");
}
parents.pop();
}
});
if (parents.length) {
throw new Error("Entered more nodes than left.");
}
},
/**
* Attempts to convert an ExpressionStatement to likely global variable
* definitions.
*
* @param {Object} node
* The AST node to convert.
* @param {boolean} isGlobal
* True if the current node is in the global scope.
* @param {String} repository
* The root of the repository.
*
* @return {Array}
* An array of variable names defined.
*/
convertExpressionToGlobals: function(node, isGlobal, repository) {
if (!modules) {
modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json"));
}
try {
var source = this.getASTSource(node);
}
catch (e) {
return [];
}
for (var reg of definitions) {
var match = source.match(reg);
if (match) {
// Must be in the global scope
if (!isGlobal) {
return [];
}
return [match[1]];
}
}
for (reg of imports) {
var match = source.match(reg);
if (match) {
// The two argument form is only acceptable in the global scope
if (node.expression.arguments.length > 1 && !isGlobal) {
return [];
}
if (match[1] in modules) {
return modules[match[1]];
}
return [match[2]];
}
}
return [];
},
/**
* Add a variable to the current scope.
* HACK: This relies on eslint internals so it could break at any time.
*
* @param {String} name
* The variable name to add to the scope.
* @param {ASTScope} scope
* The scope to add to.
* @param {boolean} writable
* Whether the global can be overwritten.
*/
addVarToScope: function(name, scope, writable) {
scope.__defineGeneric(name, scope.set, scope.variables, null, null);
let variable = scope.set.get(name);
variable.eslintExplicitGlobal = false;
variable.writeable = writable;
// Walk to the global scope which holds all undeclared variables.
while (scope.type != "global") {
scope = scope.upper;
}
// "through" contains all references with no found definition.
scope.through = scope.through.filter(function(reference) {
if (reference.identifier.name != name) {
return true;
}
// Links the variable and the reference.
// And this reference is removed from `Scope#through`.
reference.resolved = variable;
variable.references.push(reference);
return false;
});
},
/**
* Adds a set of globals to a scope.
*
* @param {Array} globalVars
* An array of global variable names.
* @param {ASTScope} scope
* The scope.
*/
addGlobals: function(globalVars, scope) {
globalVars.forEach(v => this.addVarToScope(v.name, scope, v.writable));
},
/**
* To allow espree to parse almost any JavaScript we need as many features as
* possible turned on. This method returns that config.
*
* @return {Object}
* Espree compatible permissive config.
*/
getPermissiveConfig: function() {
return {
range: true,
loc: true,
comment: true,
attachComment: true,
ecmaVersion: 8,
sourceType: "script",
ecmaFeatures: {
experimentalObjectRestSpread: true,
globalReturn: true,
}
};
},
/**
* Check whether the context is the global scope.
*
* @param {Array} ancestors
* The parents of the current node.
*
* @return {Boolean}
* True or false
*/
getIsGlobalScope: function(ancestors) {
for (let parent of ancestors) {
if (parent.type == "FunctionExpression" ||
parent.type == "FunctionDeclaration") {
return false;
}
}
return true;
},
/**
* Check whether we might be in a test head file.
*
* @param {RuleContext} scope
* You should pass this from within a rule
* e.g. helpers.getIsHeadFile(this)
*
* @return {Boolean}
* True or false
*/
getIsHeadFile: function(scope) {
var pathAndFilename = this.cleanUpPath(scope.getFilename());
return /.*[\\/]head(_.+)?\.js$/.test(pathAndFilename);
},
/**
* Gets the head files for a potential test file
*
* @param {RuleContext} scope
* You should pass this from within a rule
* e.g. helpers.getIsHeadFile(this)
*
* @return {String[]}
* Paths to head files to load for the test
*/
getTestHeadFiles: function(scope) {
if (!this.getIsTest(scope)) {
return [];
}
let filepath = this.cleanUpPath(scope.getFilename());
let dir = path.dirname(filepath);
let names = fs.readdirSync(dir)
.filter(name => name.startsWith("head") && name.endsWith(".js"))
.map(name => path.join(dir, name));
return names;
},
/**
* Gets all the test manifest data for a directory
*
* @param {String} dir
* The directory
*
* @return {Array}
* An array of objects with file and manifest properties
*/
getManifestsForDirectory: function(dir) {
if (directoryManifests.has(dir)) {
return directoryManifests.get(dir);
}
let manifests = [];
let names = fs.readdirSync(dir);
for (let name of names) {
if (!name.endsWith(".ini")) {
continue;
}
try {
let manifest = ini.parse(fs.readFileSync(path.join(dir, name), 'utf8'));
manifests.push({
file: path.join(dir, name),
manifest
})
} catch (e) {
}
}
directoryManifests.set(dir, manifests);
return manifests;
},
/**
* Gets the manifest file a test is listed in
*
* @param {RuleContext} scope
* You should pass this from within a rule
* e.g. helpers.getIsHeadFile(this)
*
* @return {String}
* The path to the test manifest file
*/
getTestManifest: function(scope) {
let filepath = this.cleanUpPath(scope.getFilename());
let dir = path.dirname(filepath);
let filename = path.basename(filepath);
for (let manifest of this.getManifestsForDirectory(dir)) {
if (filename in manifest.manifest) {
return manifest.file;
}
}
return null;
},
/**
* Check whether we are in a test of some kind.
*
* @param {RuleContext} scope
* You should pass this from within a rule
* e.g. helpers.getIsTest(this)
*
* @return {Boolean}
* True or false
*/
getIsTest: function(scope) {
// Regardless of the manifest name being in a manifest means we're a test.
let manifest = this.getTestManifest(scope);
if (manifest) {
return true;
}
return !!this.getTestType(scope);
},
/**
* Gets the type of test or null if this isn't a test.
*
* @param {RuleContext} scope
* You should pass this from within a rule
* e.g. helpers.getIsHeadFile(this)
*
* @return {String or null}
* Test type: xpcshell, browser, chrome, mochitest
*/
getTestType: function(scope) {
let manifest = this.getTestManifest(scope);
if (manifest) {
let name = path.basename(manifest);
for (let testType of ["browser", "xpcshell", "chrome", "mochitest"]) {
if (name.startsWith(testType)) {
return testType;
}
}
}
let filepath = this.cleanUpPath(scope.getFilename());
let filename = path.basename(filepath);
if (filename.startsWith("browser_")) {
return "browser";
}
if (filename.startsWith("test_")) {
return "xpcshell";
}
return null;
},
/**
* Gets the root directory of the repository by walking up directories until
* a .eslintignore file is found.
* @param {String} fileName
* The absolute path of a file in the repository
*
* @return {String} The absolute path of the repository directory
*/
getRootDir: function(fileName) {
var dirName = path.dirname(fileName);
while (dirName && !fs.existsSync(path.join(dirName, ".eslintignore"))) {
dirName = path.dirname(dirName);
}
if (!dirName) {
throw new Error("Unable to find root of repository");
}
return dirName;
},
/**
* ESLint may be executed from various places: from mach, at the root of the
* repository, or from a directory in the repository when, for instance,
* executed by a text editor's plugin.
* The value returned by context.getFileName() varies because of this.
* This helper function makes sure to return an absolute file path for the
* current context, by looking at process.cwd().
* @param {Context} context
* @return {String} The absolute path
*/
getAbsoluteFilePath: function(context) {
var fileName = this.cleanUpPath(context.getFilename());
var cwd = process.cwd();
if (path.isAbsolute(fileName)) {
// Case 2: executed from the repo's root with mach:
// fileName: /path/to/mozilla/repo/a/b/c/d.js
// cwd: /path/to/mozilla/repo
return fileName;
} else if (path.basename(fileName) == fileName) {
// Case 1b: executed from a nested directory, fileName is the base name
// without any path info (happens in Atom with linter-eslint)
return path.join(cwd, fileName);
} else {
// Case 1: executed form in a nested directory, e.g. from a text editor:
// fileName: a/b/c/d.js
// cwd: /path/to/mozilla/repo/a/b/c
var dirName = path.dirname(fileName);
return cwd.slice(0, cwd.length - dirName.length) + fileName;
}
},
/**
* When ESLint is run from SublimeText, paths retrieved from
* context.getFileName contain leading and trailing double-quote characters.
* These characters need to be removed.
*/
cleanUpPath: function(path) {
return path.replace(/^"/, "").replace(/"$/, "");
}
};

View file

@ -0,0 +1,45 @@
/**
* @fileoverview A collection of rules that help enforce JavaScript coding
* standard and avoid common errors in the Mozilla project.
* 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";
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
module.exports = {
processors: {
".xml": require("../lib/processors/xbl-bindings"),
},
rules: {
"balanced-listeners": require("../lib/rules/balanced-listeners"),
"import-globals": require("../lib/rules/import-globals"),
"import-headjs-globals": require("../lib/rules/import-headjs-globals"),
"import-browserjs-globals": require("../lib/rules/import-browserjs-globals"),
"mark-test-function-used": require("../lib/rules/mark-test-function-used"),
"no-aArgs": require("../lib/rules/no-aArgs"),
"no-cpows-in-tests": require("../lib/rules/no-cpows-in-tests"),
"no-single-arg-cu-import": require("../lib/rules/no-single-arg-cu-import"),
"reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"),
"reject-some-requires": require("../lib/rules/reject-some-requires"),
"var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
},
rulesConfig: {
"balanced-listeners": 0,
"import-globals": 0,
"import-headjs-globals": 0,
"import-browserjs-globals": 0,
"mark-test-function-used": 0,
"no-aArgs": 0,
"no-cpows-in-tests": 0,
"no-single-arg-cu-import": 0,
"reject-importGlobalProperties": 0,
"reject-some-requires": 0,
"var-only-at-top-level": 0
}
};

View file

@ -0,0 +1,363 @@
/**
* @fileoverview Converts functions and handlers from XBL bindings into JS
* functions
*
* 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";
const NS_XBL = "http://www.mozilla.org/xbl";
let sax = require("sax");
// Converts sax's error message to something that eslint will understand
let errorRegex = /(.*)\nLine: (\d+)\nColumn: (\d+)\nChar: (.*)/
function parseError(err) {
let matches = err.message.match(errorRegex);
if (!matches)
return null;
return {
fatal: true,
message: matches[1],
line: parseInt(matches[2]) + 1,
column: parseInt(matches[3])
}
}
let entityRegex = /&[\w][\w-\.]*;/g;
// A simple sax listener that generates a tree of element information
function XMLParser(parser) {
this.parser = parser;
parser.onopentag = this.onOpenTag.bind(this);
parser.onclosetag = this.onCloseTag.bind(this);
parser.ontext = this.onText.bind(this);
parser.onopencdata = this.onOpenCDATA.bind(this);
parser.oncdata = this.onCDATA.bind(this);
parser.oncomment = this.onComment.bind(this);
this.document = {
local: "#document",
uri: null,
children: [],
comments: [],
}
this._currentNode = this.document;
}
XMLParser.prototype = {
parser: null,
onOpenTag: function(tag) {
let node = {
parentNode: this._currentNode,
local: tag.local,
namespace: tag.uri,
attributes: {},
children: [],
comments: [],
textContent: "",
textLine: this.parser.line,
textColumn: this.parser.column,
textEndLine: this.parser.line
}
for (let attr of Object.keys(tag.attributes)) {
if (tag.attributes[attr].uri == "") {
node.attributes[attr] = tag.attributes[attr].value;
}
}
this._currentNode.children.push(node);
this._currentNode = node;
},
onCloseTag: function(tagname) {
this._currentNode.textEndLine = this.parser.line;
this._currentNode = this._currentNode.parentNode;
},
addText: function(text) {
this._currentNode.textContent += text;
},
onText: function(text) {
// Replace entities with some valid JS token.
this.addText(text.replace(entityRegex, "null"));
},
onOpenCDATA: function() {
// Turn the CDATA opening tag into whitespace for indent alignment
this.addText(" ".repeat("<![CDATA[".length));
},
onCDATA: function(text) {
this.addText(text);
},
onComment: function(text) {
this._currentNode.comments.push(text);
}
}
// -----------------------------------------------------------------------------
// Processor Definition
// -----------------------------------------------------------------------------
const INDENT_LEVEL = 2;
function indent(count) {
return " ".repeat(count * INDENT_LEVEL);
}
// Stores any XML parse error
let xmlParseError = null;
// Stores the lines of JS code generated from the XBL
let scriptLines = [];
// Stores a map from the synthetic line number to the real line number
// and column offset.
let lineMap = [];
function addSyntheticLine(line, linePos, addDisableLine) {
lineMap[scriptLines.length] = { line: linePos, offset: null };
scriptLines.push(line + (addDisableLine ? "" : " // eslint-disable-line"));
}
/**
* Adds generated lines from an XBL node to the script to be passed back to eslint.
*/
function addNodeLines(node, reindent) {
let lines = node.textContent.split("\n");
let startLine = node.textLine;
let startColumn = node.textColumn;
// The case where there are no whitespace lines before the first text is
// treated differently for indentation
let indentFirst = false;
// Strip off any preceeding whitespace only lines. These are often used to
// format the XML and CDATA blocks.
while (lines.length && lines[0].trim() == "") {
indentFirst = true;
startLine++;
lines.shift();
}
// Strip off any whitespace lines at the end. These are often used to line
// up the closing tags
while (lines.length && lines[lines.length - 1].trim() == "") {
lines.pop();
}
if (!indentFirst) {
let firstLine = lines.shift();
firstLine = " ".repeat(reindent * INDENT_LEVEL) + firstLine;
// ESLint counts columns starting at 1 rather than 0
lineMap[scriptLines.length] = { line: startLine, offset: reindent * INDENT_LEVEL - (startColumn - 1) };
scriptLines.push(firstLine);
startLine++;
}
// Find the preceeding whitespace for all lines that aren't entirely whitespace
let indents = lines.filter(s => s.trim().length > 0)
.map(s => s.length - s.trimLeft().length);
// Find the smallest indent level in use
let minIndent = Math.min.apply(null, indents);
for (let line of lines) {
if (line.trim().length == 0) {
// Don't offset lines that are only whitespace, the only possible JS error
// is trailing whitespace and we want it to point at the right place
lineMap[scriptLines.length] = { line: startLine, offset: 0 };
} else {
line = " ".repeat(reindent * INDENT_LEVEL) + line.substring(minIndent);
lineMap[scriptLines.length] = { line: startLine, offset: reindent * INDENT_LEVEL - (minIndent - 1) };
}
scriptLines.push(line);
startLine++;
}
}
module.exports = {
preprocess: function(text, filename) {
xmlParseError = null;
scriptLines = [];
lineMap = [];
// Non-strict allows us to ignore many errors from entities and
// preprocessing at the expense of failing to report some XML errors.
// Unfortunately it also throws away the case of tagnames and attributes
let parser = sax.parser(false, {
lowercase: true,
xmlns: true,
});
parser.onerror = function(err) {
xmlParseError = parseError(err);
}
let xp = new XMLParser(parser);
parser.write(text);
// Sanity checks to make sure we're dealing with an XBL document
let document = xp.document;
if (document.children.length != 1) {
return [];
}
let bindings = document.children[0];
if (bindings.local != "bindings" || bindings.namespace != NS_XBL) {
return [];
}
for (let comment of document.comments) {
addSyntheticLine(`/*`, 0, true);
for (let line of comment.split("\n")) {
addSyntheticLine(`${line.trim()}`, 0, true);
}
addSyntheticLine(`*/`, 0, true);
}
addSyntheticLine(`this.bindings = {`, bindings.textLine);
for (let binding of bindings.children) {
if (binding.local != "binding" || binding.namespace != NS_XBL) {
continue;
}
addSyntheticLine(indent(1) + `"${binding.attributes.id}": {`, binding.textLine);
for (let part of binding.children) {
if (part.namespace != NS_XBL) {
continue;
}
if (part.local == "implementation") {
addSyntheticLine(indent(2) + `implementation: {`, part.textLine);
} else if (part.local == "handlers") {
addSyntheticLine(indent(2) + `handlers: [`, part.textLine);
} else {
continue;
}
for (let item of part.children) {
if (item.namespace != NS_XBL) {
continue;
}
switch (item.local) {
case "field": {
// Fields are something like lazy getter functions
// Ignore empty fields
if (item.textContent.trim().length == 0) {
continue;
}
addSyntheticLine(indent(3) + `get ${item.attributes.name}() {`, item.textLine);
addSyntheticLine(indent(4) + `return (`, item.textLine);
// Remove trailing semicolons, as we are adding our own
item.textContent = item.textContent.replace(/;(?=\s*$)/, "");
addNodeLines(item, 5);
addSyntheticLine(indent(4) + `);`, item.textLine);
addSyntheticLine(indent(3) + `},`, item.textEndLine);
break;
}
case "constructor":
case "destructor": {
// Constructors and destructors become function declarations
addSyntheticLine(indent(3) + `${item.local}() {`, item.textLine);
addNodeLines(item, 4);
addSyntheticLine(indent(3) + `},`, item.textEndLine);
break;
}
case "method": {
// Methods become function declarations with the appropriate params
let params = item.children.filter(n => n.local == "parameter" && n.namespace == NS_XBL)
.map(n => n.attributes.name)
.join(", ");
let body = item.children.filter(n => n.local == "body" && n.namespace == NS_XBL)[0];
addSyntheticLine(indent(3) + `${item.attributes.name}(${params}) {`, item.textLine);
addNodeLines(body, 4);
addSyntheticLine(indent(3) + `},`, item.textEndLine);
break;
}
case "property": {
// Properties become one or two function declarations
for (let propdef of item.children) {
if (propdef.namespace != NS_XBL) {
continue;
}
if (propdef.local == "setter") {
addSyntheticLine(indent(3) + `set ${item.attributes.name}(val) {`, propdef.textLine);
} else if (propdef.local == "getter") {
addSyntheticLine(indent(3) + `get ${item.attributes.name}() {`, propdef.textLine);
} else {
continue;
}
addNodeLines(propdef, 4);
addSyntheticLine(indent(3) + `},`, propdef.textEndLine);
}
break;
}
case "handler": {
// Handlers become a function declaration with an `event` parameter
addSyntheticLine(indent(3) + `function(event) {`, item.textLine);
addNodeLines(item, 4);
addSyntheticLine(indent(3) + `},`, item.textEndLine);
break;
}
default:
continue;
}
}
addSyntheticLine(indent(2) + (part.local == "implementation" ? `},` : `],`), part.textEndLine);
}
addSyntheticLine(indent(1) + `},`, binding.textEndLine);
}
addSyntheticLine(`};`, bindings.textEndLine);
let script = scriptLines.join("\n") + "\n";
return [script];
},
postprocess: function(messages, filename) {
// If there was an XML parse error then just return that
if (xmlParseError) {
return [xmlParseError];
}
// For every message from every script block update the line to point to the
// correct place.
let errors = [];
for (let i = 0; i < messages.length; i++) {
for (let message of messages[i]) {
// ESLint indexes lines starting at 1 but our arrays start at 0
let mapped = lineMap[message.line - 1];
message.line = mapped.line + 1;
if (mapped.offset) {
message.column -= mapped.offset;
} else {
message.column = NaN;
}
errors.push(message);
}
}
return errors;
}
};

View file

@ -0,0 +1,51 @@
"use strict";
/**
* Based on npm coding standards at https://docs.npmjs.com/misc/coding-style.
*
* The places we differ from the npm coding style:
* - Commas should be at the end of a line.
* - Always use semicolons.
* - Functions should not have whitespace before params.
*/
module.exports = {
"env": {
"node": true
},
"rules": {
"brace-style": ["error", "1tbs"],
"camelcase": "error",
"comma-dangle": ["error", "never"],
"comma-spacing": "error",
"comma-style": ["error", "last"],
"curly": ["error", "multi-line"],
"handle-callback-err": ["error", "er"],
"indent": ["error", 2, {"SwitchCase": 1}],
"max-len": ["error", 80, "error"],
"no-multiple-empty-lines": ["error", {"max": 1}],
"no-undef": "error",
"no-undef-init": "error",
"no-unexpected-multiline": "error",
"object-curly-spacing": "off",
"one-var": ["error", "never"],
"operator-linebreak": ["error", "after"],
"semi": ["error", "always"],
"space-before-blocks": "error",
"space-before-function-paren": ["error", "never"],
"keyword-spacing": "error",
"strict": ["error", "global"],
},
// Globals accessible within node modules.
"globals": {
"DTRACE_HTTP_CLIENT_REQUEST": true,
"DTRACE_HTTP_CLIENT_RESPONSE": true,
"DTRACE_HTTP_SERVER_REQUEST": true,
"DTRACE_HTTP_SERVER_RESPONSE": true,
"DTRACE_NET_SERVER_CONNECTION": true,
"DTRACE_NET_STREAM_END": true,
"Intl": true,
},
};

View file

@ -0,0 +1,113 @@
/**
* @fileoverview Check that there's a removeEventListener for each
* addEventListener and an off for each on.
* Note that for now, this rule is rather simple in that it only checks that
* for each event name there is both an add and remove listener. It doesn't
* check that these are called on the right objects or with the same callback.
*
* 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
// -----------------------------------------------------------------------------
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
var DICTIONARY = {
"addEventListener": "removeEventListener",
"on": "off"
};
// Invert this dictionary to make it easy later.
var INVERTED_DICTIONARY = {};
for (var i in DICTIONARY) {
INVERTED_DICTIONARY[DICTIONARY[i]] = i;
}
// Collect the add/remove listeners in these 2 arrays.
var addedListeners = [];
var removedListeners = [];
function addAddedListener(node) {
addedListeners.push({
functionName: node.callee.property.name,
type: node.arguments[0].value,
node: node.callee.property,
useCapture: node.arguments[2] ? node.arguments[2].value : null
});
}
function addRemovedListener(node) {
removedListeners.push({
functionName: node.callee.property.name,
type: node.arguments[0].value,
useCapture: node.arguments[2] ? node.arguments[2].value : null
});
}
function getUnbalancedListeners() {
var unbalanced = [];
for (var j = 0; j < addedListeners.length; j++) {
if (!hasRemovedListener(addedListeners[j])) {
unbalanced.push(addedListeners[j]);
}
}
addedListeners = removedListeners = [];
return unbalanced;
}
function hasRemovedListener(addedListener) {
for (var k = 0; k < removedListeners.length; k++) {
var listener = removedListeners[k];
if (DICTIONARY[addedListener.functionName] === listener.functionName &&
addedListener.type === listener.type &&
addedListener.useCapture === listener.useCapture) {
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
CallExpression: function(node) {
if (node.arguments.length === 0) {
return;
}
if (node.callee.type === "MemberExpression") {
var listenerMethodName = node.callee.property.name;
if (DICTIONARY.hasOwnProperty(listenerMethodName)) {
addAddedListener(node);
} else if (INVERTED_DICTIONARY.hasOwnProperty(listenerMethodName)) {
addRemovedListener(node);
}
}
},
"Program:exit": function() {
getUnbalancedListeners().forEach(function(listener) {
context.report(listener.node,
"No corresponding '{{functionName}}({{type}})' was found.",
{
functionName: DICTIONARY[listener.functionName],
type: listener.type
});
});
}
};
};

View file

@ -0,0 +1,83 @@
/**
* @fileoverview Import globals from browser.js.
*
* 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
// -----------------------------------------------------------------------------
var fs = require("fs");
var path = require("path");
var helpers = require("../helpers");
var globals = require("../globals");
const SCRIPTS = [
//"browser/base/content/nsContextMenu.js",
"toolkit/content/contentAreaUtils.js",
"browser/components/places/content/editBookmarkOverlay.js",
"toolkit/components/printing/content/printUtils.js",
"toolkit/content/viewZoomOverlay.js",
"browser/components/places/content/browserPlacesViews.js",
"browser/base/content/browser.js",
"browser/components/downloads/content/downloads.js",
"browser/components/downloads/content/indicator.js",
"browser/components/customizableui/content/panelUI.js",
"toolkit/components/viewsource/content/viewSourceUtils.js",
"browser/base/content/browser-addons.js",
"browser/base/content/browser-ctrlTab.js",
"browser/base/content/browser-customization.js",
"browser/base/content/browser-devedition.js",
"browser/base/content/browser-feeds.js",
"browser/base/content/browser-fullScreenAndPointerLock.js",
"browser/base/content/browser-fullZoom.js",
"browser/base/content/browser-gestureSupport.js",
"browser/base/content/browser-media.js",
"browser/base/content/browser-places.js",
"browser/base/content/browser-plugins.js",
"browser/base/content/browser-refreshblocker.js",
"browser/base/content/browser-safebrowsing.js",
"browser/base/content/browser-sidebar.js",
"browser/base/content/browser-social.js",
"browser/base/content/browser-syncui.js",
"browser/base/content/browser-tabsintitlebar.js",
"browser/base/content/browser-thumbnails.js",
"browser/base/content/browser-trackingprotection.js",
"browser/base/content/browser-data-submission-info-bar.js",
"browser/base/content/browser-fxaccounts.js"
];
module.exports = function(context) {
return {
Program: function(node) {
if (helpers.getTestType(this) != "browser" &&
!helpers.getIsHeadFile(this)) {
return;
}
let filepath = helpers.getAbsoluteFilePath(context);
let root = helpers.getRootDir(filepath);
for (let script of SCRIPTS) {
let fileName = path.join(root, script);
try {
let newGlobals = globals.getGlobalsForFile(fileName);
helpers.addGlobals(newGlobals, context.getScope());
} catch (e) {
context.report(
node,
"Could not load globals from file {{filePath}}: {{error}}",
{
filePath: path.relative(root, fileName),
error: e
}
);
}
}
}
};
};

View file

@ -0,0 +1,15 @@
/**
* @fileoverview Discovers all globals for the current file.
*
* 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
// -----------------------------------------------------------------------------
module.exports = require("../globals").getESLintGlobalParser;

View file

@ -0,0 +1,49 @@
/**
* @fileoverview Import globals from head.js and from any files that were
* imported by head.js (as far as we can correctly resolve the path).
*
* 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
// -----------------------------------------------------------------------------
var fs = require("fs");
var path = require("path");
var helpers = require("../helpers");
var globals = require("../globals");
module.exports = function(context) {
function importHead(path, node) {
try {
let stats = fs.statSync(path);
if (!stats.isFile()) {
return;
}
} catch (e) {
return;
}
let newGlobals = globals.getGlobalsForFile(path);
helpers.addGlobals(newGlobals, context.getScope());
}
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
Program: function(node) {
let heads = helpers.getTestHeadFiles(this);
for (let head of heads) {
importHead(head, node);
}
}
};
};

View file

@ -0,0 +1,37 @@
/**
* @fileoverview Simply marks `test` (the test method) or `run_test` as used when
* in mochitests or xpcshell tests respectively. This avoids ESLint telling us
* that the function is never called.
*
* 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
// -----------------------------------------------------------------------------
var helpers = require("../helpers");
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
Program: function() {
if (helpers.getTestType(this) == "browser") {
context.markVariableAsUsed("test");
return;
}
if (helpers.getTestType(this) == "xpcshell") {
context.markVariableAsUsed("run_test");
return;
}
}
};
};

View file

@ -0,0 +1,55 @@
/**
* @fileoverview warns against using hungarian notation in function arguments
* (i.e. aArg).
*
* 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
// -----------------------------------------------------------------------------
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function isPrefixed(name) {
return name.length >= 2 && /^a[A-Z]/.test(name);
}
function deHungarianize(name) {
return name.substring(1, 2).toLowerCase() +
name.substring(2, name.length);
}
function checkFunction(node) {
for (var i = 0; i < node.params.length; i++) {
var param = node.params[i];
if (param.name && isPrefixed(param.name)) {
var errorObj = {
name: param.name,
suggestion: deHungarianize(param.name)
};
context.report(param,
"Parameter '{{name}}' uses Hungarian Notation, " +
"consider using '{{suggestion}}' instead.",
errorObj);
}
}
}
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
"FunctionDeclaration": checkFunction,
"ArrowFunctionExpression": checkFunction,
"FunctionExpression": checkFunction
};
};

View file

@ -0,0 +1,112 @@
/**
* @fileoverview Prevent access to CPOWs in browser mochitests.
*
* 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
// -----------------------------------------------------------------------------
var helpers = require("../helpers");
var cpows = [
/^gBrowser\.contentWindow/,
/^gBrowser\.contentDocument/,
/^gBrowser\.selectedBrowser.contentWindow/,
/^browser\.contentDocument/,
/^window\.content/
];
var isInContentTask = false;
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function showError(node, identifier) {
if (isInContentTask) {
return;
}
context.report({
node: node,
message: identifier +
" is a possible Cross Process Object Wrapper (CPOW)."
});
}
function isContentTask(node) {
return node &&
node.type === "MemberExpression" &&
node.property.type === "Identifier" &&
node.property.name === "spawn" &&
node.object.type === "Identifier" &&
node.object.name === "ContentTask";
}
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
CallExpression: function(node) {
if (isContentTask(node.callee)) {
isInContentTask = true;
}
},
"CallExpression:exit": function(node) {
if (isContentTask(node.callee)) {
isInContentTask = false;
}
},
MemberExpression: function(node) {
if (helpers.getTestType(this) != "browser") {
return;
}
var expression = context.getSource(node);
// Only report a single CPOW error per node -- so if checking
// |cpows| reports one, don't report another below.
var someCpowFound = cpows.some(function(cpow) {
if (cpow.test(expression)) {
showError(node, expression);
return true;
}
return false;
});
if (!someCpowFound && helpers.getIsGlobalScope(context.getAncestors())) {
if (/^content\./.test(expression)) {
showError(node, expression);
return;
}
}
},
Identifier: function(node) {
if (helpers.getTestType(this) != "browser") {
return;
}
var expression = context.getSource(node);
if (expression == "content" || /^content\./.test(expression)) {
if (node.parent.type === "MemberExpression" &&
node.parent.object &&
node.parent.object.type === "Identifier" &&
node.parent.object.name != "content") {
return;
}
showError(node, expression);
return;
}
}
};
};

View file

@ -0,0 +1,39 @@
/**
* @fileoverview Reject use of single argument Cu.import
*
* 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
// -----------------------------------------------------------------------------
var helpers = require("../helpers");
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
"CallExpression": function(node) {
if (node.callee.type === "MemberExpression") {
let memexp = node.callee;
if (memexp.object.type === "Identifier" &&
// Only Cu, not Components.utils; see bug 1230369.
memexp.object.name === "Cu" &&
memexp.property.type === "Identifier" &&
memexp.property.name === "import" &&
node.arguments.length === 1) {
context.report(node, "Single argument Cu.import exposes new " +
"globals to all modules");
}
}
}
};
};

View file

@ -0,0 +1,37 @@
/**
* @fileoverview Reject use of Cu.importGlobalProperties
*
* 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
// -----------------------------------------------------------------------------
var helpers = require("../helpers");
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
"CallExpression": function(node) {
if (node.callee.type === "MemberExpression") {
let memexp = node.callee;
if (memexp.object.type === "Identifier" &&
// Only Cu, not Components.utils; see bug 1230369.
memexp.object.name === "Cu" &&
memexp.property.type === "Identifier" &&
memexp.property.name === "importGlobalProperties") {
context.report(node, "Unexpected call to Cu.importGlobalProperties");
}
}
}
};
};

View file

@ -0,0 +1,48 @@
/**
* @fileoverview Reject some uses of require.
*
* 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
// -----------------------------------------------------------------------------
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
if (typeof(context.options[0]) !== "string") {
throw new Error("reject-some-requires expects a regexp");
}
const RX = new RegExp(context.options[0]);
const checkPath = function(node, path) {
if (RX.test(path)) {
context.report(node, `require(${path}) is not allowed`);
}
};
return {
"CallExpression": function(node) {
if (node.callee.type == "Identifier" &&
node.callee.name == "require" &&
node.arguments.length == 1 &&
node.arguments[0].type == "Literal") {
checkPath(node, node.arguments[0].value);
} else if (node.callee.type == "MemberExpression" &&
node.callee.property.type == "Identifier" &&
node.callee.property.name == "lazyRequireGetter" &&
node.arguments.length >= 3 &&
node.arguments[2].type == "Literal") {
checkPath(node, node.arguments[2].value);
}
}
};
};

View file

@ -0,0 +1,34 @@
/**
* @fileoverview Marks all var declarations that are not at the top level
* invalid.
*
* 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
// -----------------------------------------------------------------------------
var helpers = require("../helpers");
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
"VariableDeclaration": function(node) {
if (node.kind === "var") {
if (helpers.getIsGlobalScope(context.getAncestors())) {
return;
}
context.report(node, "Unexpected var, use let or const instead.");
}
}
};
};

View file

@ -0,0 +1,29 @@
{
"name": "eslint-plugin-mozilla",
"version": "0.2.5",
"description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin",
"mozilla",
"firefox"
],
"bugs": {
"url": "https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer%20Tools"
},
"homepage": "https://bugzilla.mozilla.org/show_bug.cgi?id=1203520",
"author": "Mike Ratcliffe",
"main": "lib/index.js",
"dependencies": {
"escope": "^3.6.0",
"espree": "^3.2.0",
"estraverse": "^4.2.0",
"ini-parser": "^0.0.2",
"sax": "^1.1.4"
},
"engines": {
"node": ">=0.10.0"
},
"license": "MPL-2.0"
}