use-chromeutils-generateqi.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /**
  2. * @fileoverview Reject use of XPCOMUtils.generateQI and JS-implemented
  3. * QueryInterface methods in favor of ChromeUtils.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. */
  9. "use strict";
  10. function isIdentifier(node, id) {
  11. return node && node.type === "Identifier" && node.name === id;
  12. }
  13. function isMemberExpression(node, object, member) {
  14. return (
  15. node.type === "MemberExpression" &&
  16. isIdentifier(node.object, object) &&
  17. isIdentifier(node.property, member)
  18. );
  19. }
  20. const MSG_NO_JS_QUERY_INTERFACE =
  21. "Please use ChromeUtils.generateQI rather than manually creating " +
  22. "JavaScript QueryInterface functions";
  23. const MSG_NO_XPCOMUTILS_GENERATEQI =
  24. "Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI";
  25. function funcToGenerateQI(context, node) {
  26. const sourceCode = context.getSourceCode();
  27. const text = sourceCode.getText(node);
  28. let interfaces = [];
  29. let match;
  30. let re = /\bCi\.([a-zA-Z0-9]+)\b|\b(nsI[A-Z][a-zA-Z0-9]+)\b/g;
  31. while ((match = re.exec(text))) {
  32. interfaces.push(match[1] || match[2]);
  33. }
  34. let ifaces = interfaces
  35. .filter(iface => iface != "nsISupports")
  36. .map(iface => JSON.stringify(iface))
  37. .join(", ");
  38. return `ChromeUtils.generateQI([${ifaces}])`;
  39. }
  40. module.exports = {
  41. meta: {
  42. docs: {
  43. url:
  44. "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/use-chromeutils-generateqi.html",
  45. },
  46. fixable: "code",
  47. type: "suggestion",
  48. },
  49. create(context) {
  50. return {
  51. CallExpression(node) {
  52. let { callee } = node;
  53. if (isMemberExpression(callee, "XPCOMUtils", "generateQI")) {
  54. context.report({
  55. node,
  56. message: MSG_NO_XPCOMUTILS_GENERATEQI,
  57. fix(fixer) {
  58. return fixer.replaceText(callee, "ChromeUtils.generateQI");
  59. },
  60. });
  61. }
  62. },
  63. "AssignmentExpression > MemberExpression[property.name='QueryInterface']": function(
  64. node
  65. ) {
  66. const { right } = node.parent;
  67. if (right.type === "FunctionExpression") {
  68. context.report({
  69. node: node.parent,
  70. message: MSG_NO_JS_QUERY_INTERFACE,
  71. fix(fixer) {
  72. return fixer.replaceText(right, funcToGenerateQI(context, right));
  73. },
  74. });
  75. }
  76. },
  77. "Property[key.name='QueryInterface'][value.type='FunctionExpression']": function(
  78. node
  79. ) {
  80. context.report({
  81. node,
  82. message: MSG_NO_JS_QUERY_INTERFACE,
  83. fix(fixer) {
  84. let generateQI = funcToGenerateQI(context, node.value);
  85. return fixer.replaceText(node, `QueryInterface: ${generateQI}`);
  86. },
  87. });
  88. },
  89. };
  90. },
  91. };