summaryrefslogtreecommitdiffstats
path: root/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-generateqi.js
blob: 6d72f785decf69fb64408bf5e06ae7b6c46c8584 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
 * @fileoverview Reject use of XPCOMUtils.generateQI and JS-implemented
 *               QueryInterface methods 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)
  );
}

const MSG_NO_JS_QUERY_INTERFACE =
  "Please use ChromeUtils.generateQI rather than manually creating " +
  "JavaScript QueryInterface functions";

const MSG_NO_XPCOMUTILS_GENERATEQI =
  "Please use ChromeUtils.generateQI instead of XPCOMUtils.generateQI";

function funcToGenerateQI(context, node) {
  const sourceCode = context.getSourceCode();
  const text = sourceCode.getText(node);

  let interfaces = [];
  let match;
  let re = /\bCi\.([a-zA-Z0-9]+)\b|\b(nsI[A-Z][a-zA-Z0-9]+)\b/g;
  while ((match = re.exec(text))) {
    interfaces.push(match[1] || match[2]);
  }

  let ifaces = interfaces
    .filter(iface => iface != "nsISupports")
    .map(iface => JSON.stringify(iface))
    .join(", ");

  return `ChromeUtils.generateQI([${ifaces}])`;
}

module.exports = {
  meta: {
    fixable: "code",
  },

  create(context) {
    return {
      CallExpression(node) {
        let { callee } = node;
        if (isMemberExpression(callee, "XPCOMUtils", "generateQI")) {
          context.report({
            node,
            message: MSG_NO_XPCOMUTILS_GENERATEQI,
            fix(fixer) {
              return fixer.replaceText(callee, "ChromeUtils.generateQI");
            },
          });
        }
      },

      "AssignmentExpression > MemberExpression[property.name='QueryInterface']": function(
        node
      ) {
        const { right } = node.parent;
        if (right.type === "FunctionExpression") {
          context.report({
            node: node.parent,
            message: MSG_NO_JS_QUERY_INTERFACE,
            fix(fixer) {
              return fixer.replaceText(right, funcToGenerateQI(context, right));
            },
          });
        }
      },

      "Property[key.name='QueryInterface'][value.type='FunctionExpression']": function(
        node
      ) {
        context.report({
          node,
          message: MSG_NO_JS_QUERY_INTERFACE,
          fix(fixer) {
            let generateQI = funcToGenerateQI(context, node.value);
            return fixer.replaceText(node, `QueryInterface: ${generateQI}`);
          },
        });
      },
    };
  },
};