valid-ci-uses.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * @fileoverview Reject uses of unknown interfaces on Ci and properties of those
  3. * interfaces.
  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. const os = require("os");
  11. const helpers = require("../helpers");
  12. // These interfaces are all platform specific, so may be not present
  13. // on all platforms.
  14. const platformSpecificInterfaces = new Map([
  15. ["nsIAboutThirdParty", "windows"],
  16. ["nsIAboutWindowsMessages", "windows"],
  17. ["nsIJumpListItem", "windows"],
  18. ["nsIJumpListLink", "windows"],
  19. ["nsIJumpListSeparator", "windows"],
  20. ["nsIJumpListShortcut", "windows"],
  21. ["nsITaskbarWindowPreview", "windows"],
  22. ["nsIWindowsAlertsService", "windows"],
  23. ["nsIWinAppHelper", "windows"],
  24. ["nsIWinTaskbar", "windows"],
  25. ["nsIWinTaskSchedulerService", "windows"],
  26. ["nsIWindowsRegKey", "windows"],
  27. ["nsIWindowsPackageManager", "windows"],
  28. ["nsIWindowsShellService", "windows"],
  29. ["nsIAccessibleMacEvent", "darwin"],
  30. ["nsIAccessibleMacInterface", "darwin"],
  31. ["nsILocalFileMac", "darwin"],
  32. ["nsIAccessibleMacEvent", "darwin"],
  33. ["nsIMacAttributionService", "darwin"],
  34. ["nsIMacShellService", "darwin"],
  35. ["nsIMacDockSupport", "darwin"],
  36. ["nsIMacFinderProgress", "darwin"],
  37. ["nsIMacPreferencesReader", "darwin"],
  38. ["nsIMacSharingService", "darwin"],
  39. ["nsIMacUserActivityUpdater", "darwin"],
  40. ["nsIMacWebAppUtils", "darwin"],
  41. ["nsIStandaloneNativeMenu", "darwin"],
  42. ["nsITouchBarHelper", "darwin"],
  43. ["nsITouchBarInput", "darwin"],
  44. ["nsITouchBarUpdater", "darwin"],
  45. ["mozISandboxReporter", "linux"],
  46. ["nsIApplicationChooser", "linux"],
  47. ["nsIGNOMEShellService", "linux"],
  48. ["nsIGtkTaskbarProgress", "linux"],
  49. // These are used in the ESLint test code.
  50. ["amIFoo", "any"],
  51. ["nsIMeh", "any"],
  52. // Can't easily detect android builds from ESLint at the moment.
  53. ["nsIAndroidBridge", "any"],
  54. ["nsIAndroidView", "any"],
  55. // Code coverage is enabled only for certain builds (MOZ_CODE_COVERAGE).
  56. ["nsICodeCoverage", "any"],
  57. // Layout debugging is enabled only for certain builds (MOZ_LAYOUT_DEBUGGER).
  58. ["nsILayoutDebuggingTools", "any"],
  59. // Sandbox test is only enabled for certain configurations (MOZ_SANDBOX,
  60. // MOZ_DEBUG, ENABLE_TESTS).
  61. ["mozISandboxTest", "any"],
  62. ]);
  63. function interfaceHasProperty(interfaceName, propertyName) {
  64. // `Ci.nsIFoo.number` is valid, it returns the iid.
  65. if (propertyName == "number") {
  66. return true;
  67. }
  68. let interfaceInfo = helpers.xpidlData.get(interfaceName);
  69. if (!interfaceInfo) {
  70. return true;
  71. }
  72. // If the property is not in the lists of consts for this interface, check
  73. // any parents as well.
  74. if (!interfaceInfo.consts.find(e => e.name === propertyName)) {
  75. if (interfaceInfo.parent && interfaceInfo.parent != "nsISupports") {
  76. return interfaceHasProperty(interfaceName.parent, propertyName);
  77. }
  78. return false;
  79. }
  80. return true;
  81. }
  82. module.exports = {
  83. meta: {
  84. docs: {
  85. url:
  86. "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/valid-ci-uses.html",
  87. },
  88. messages: {
  89. missingInterface:
  90. "{{ interface }} is defined in this rule's platform specific list, but is not available",
  91. unknownInterface: "Use of unknown interface Ci.{{ interface}}",
  92. unknownProperty:
  93. "Use of unknown property Ci.{{ interface }}.{{ property }}",
  94. },
  95. type: "problem",
  96. },
  97. create(context) {
  98. return {
  99. MemberExpression(node) {
  100. if (
  101. node.computed === false &&
  102. node.type === "MemberExpression" &&
  103. node.object.type === "Identifier" &&
  104. node.object.name === "Ci" &&
  105. node.property.type === "Identifier" &&
  106. node.property.name.includes("I")
  107. ) {
  108. if (!helpers.xpidlData.get(node.property.name)) {
  109. let platformSpecific = platformSpecificInterfaces.get(
  110. node.property.name
  111. );
  112. if (!platformSpecific) {
  113. context.report({
  114. node,
  115. messageId: "unknownInterface",
  116. data: {
  117. interface: node.property.name,
  118. },
  119. });
  120. } else if (platformSpecific == os.platform) {
  121. context.report({
  122. node,
  123. messageId: "missingInterface",
  124. data: {
  125. interface: node.property.name,
  126. },
  127. });
  128. }
  129. }
  130. }
  131. if (
  132. node.computed === false &&
  133. node.object.type === "MemberExpression" &&
  134. node.object.object.type === "Identifier" &&
  135. node.object.object.name === "Ci" &&
  136. node.object.property.type === "Identifier" &&
  137. node.object.property.name.includes("I") &&
  138. node.property.type === "Identifier"
  139. ) {
  140. if (
  141. !interfaceHasProperty(node.object.property.name, node.property.name)
  142. ) {
  143. context.report({
  144. node,
  145. messageId: "unknownProperty",
  146. data: {
  147. interface: node.object.property.name,
  148. property: node.property.name,
  149. },
  150. });
  151. }
  152. }
  153. },
  154. };
  155. },
  156. };