Bug 1460092: Add ESLint rule to enforce use of ChromeUtils.generateQI. r=Gijs
authorKris Maglione <maglione.k@gmail.com>
Tue, 08 May 2018 18:36:22 -0700
changeset 417671 4dae882935055cd75a80139b0e17e232072b763c
parent 417670 91303e1d488f27a83186981dbf875a954a6be64a
child 417672 5c5c9cc183a0a4cd14a4a16aab4ec228adbffb16
push id33977
push userncsoregi@mozilla.com
push dateThu, 10 May 2018 16:43:24 +0000
treeherdermozilla-central@17db33b6a124 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1460092
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1460092: Add ESLint rule to enforce use of ChromeUtils.generateQI. r=Gijs Also fixes existing code which fails the rule. MozReview-Commit-ID: CkLFgsspGMU
accessible/tests/mochitest/autocomplete.js
browser/base/content/test/general/browser_keywordSearch.js
browser/base/content/test/urlbar/browser_bug623155.js
browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
browser/components/originattributes/test/browser/browser_cache.js
browser/components/originattributes/test/browser/browser_sanitize.js
browser/components/places/content/treeView.js
browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
browser/components/uitour/test/browser_UITour_modalDialog.js
browser/modules/test/browser/browser_ProcessHangNotifications.js
devtools/client/jsonview/converter-child.js
devtools/client/jsonview/converter-observer.js
devtools/client/responsive.html/browser/web-navigation.js
devtools/client/shared/test/shared-head.js
devtools/server/actors/csscoverage.js
devtools/server/actors/highlighters.js
devtools/server/actors/reflow.js
devtools/server/actors/tab.js
devtools/server/actors/webconsole/content-process-forward.js
devtools/server/actors/webconsole/listeners.js
devtools/server/actors/webextension-inspected-window.js
devtools/server/actors/worker.js
devtools/server/tests/unit/test_layout-reflows-observer.js
devtools/shared/webconsole/network-monitor.js
devtools/shared/webconsole/test/unit/test_security-info-parser.js
devtools/shared/webconsole/test/unit/test_security-info-state.js
devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
devtools/shared/webconsole/test/unit/test_throttle.js
devtools/shared/webconsole/throttle.js
devtools/startup/devtools-startup.js
mobile/android/tests/browser/robocop/.eslintrc.js
security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_ocsp_private_caching.js
services/fxaccounts/tests/xpcshell/test_push_service.js
testing/marionette/test/unit/test_cookie.js
testing/mochitest/browser-test.js
testing/mochitest/server.js
testing/modules/AppInfo.jsm
testing/xpcshell/head.js
toolkit/components/autocomplete/tests/unit/test_378079.js
toolkit/components/autocomplete/tests/unit/test_393191.js
toolkit/components/autocomplete/tests/unit/test_440866.js
toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
toolkit/components/autocomplete/tests/unit/test_previousResult.js
toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
toolkit/components/passwordmgr/test/prompt_common.js
toolkit/components/passwordmgr/test/test_prompt_async.html
toolkit/components/places/tests/unit/test_000_frecency.js
toolkit/components/places/tests/unit/test_408221.js
toolkit/components/places/tests/unit/test_413784.js
toolkit/components/places/tests/unit/test_adaptive_bug527311.js
toolkit/components/places/tests/unit/test_frecency.js
toolkit/components/places/tests/unit/test_tag_autocomplete_search.js
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
toolkit/components/url-classifier/nsUrlClassifierListManager.js
toolkit/components/url-classifier/tests/browser/classifierHelper.js
toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
toolkit/components/url-classifier/tests/unit/test_partial.js
toolkit/components/url-classifier/tests/unit/test_streamupdater.js
toolkit/components/windowcreator/tests/unit/test_wwauthpromptfactory.js
toolkit/content/widgets/editor.xml
toolkit/content/widgets/findbar.xml
toolkit/crashreporter/test/browser/head.js
toolkit/crashreporter/test/unit/test_crash_with_memory_report.js
toolkit/modules/Integration.jsm
toolkit/mozapps/extensions/test/browser/head.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
toolkit/mozapps/update/content/updates.js
toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-generateqi.js
tools/lint/eslint/eslint-plugin-mozilla/package.json
tools/lint/eslint/eslint-plugin-mozilla/tests/use-chromeutils-generateqi.js
xpcom/tests/unit/test_bug1434856.js
xpcom/tests/unit/test_bug374754.js
--- a/accessible/tests/mochitest/autocomplete.js
+++ b/accessible/tests/mochitest/autocomplete.js
@@ -108,24 +108,17 @@ AutoCompleteSearch.prototype =
   startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
     var result = this.allResults.getAutoCompleteResultFor(aSearchString);
     aListener.onSearchResult(this, result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(nsISupports) ||
-        iid.equals(nsIFactory) ||
-        iid.equals(nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   },
 
   // Search name. Used by AutoCompleteController.
   name: null,
@@ -183,20 +176,14 @@ AutoCompleteResult.prototype =
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(nsISupports) ||
-        iid.equals(nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
 
   // Data
   values: null,
   comments: null
 };
--- a/browser/base/content/test/general/browser_keywordSearch.js
+++ b/browser/base/content/test/general/browser_keywordSearch.js
@@ -46,26 +46,17 @@ function test() {
                        Ci.nsIWebProgressListener.STATE_START;
         if (!(flags & docStart)) {
           return;
         }
 
         req.cancel(Cr.NS_ERROR_FAILURE);
       },
 
-      QueryInterface: function QueryInterface(aIID) {
-        if (aIID.equals(Ci.nsIWebProgressListener) ||
-            aIID.equals(Ci.nsIWebProgressListener2) ||
-            aIID.equals(Ci.nsISupportsWeakReference) ||
-            aIID.equals(Ci.nsISupports)) {
-          return this;
-        }
-
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      }
+      QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsIWebProgressListener2", "nsISupportsWeakReference"])
     };
 
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(listener,
                                     Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
 
     addEventListener("unload", () => {
--- a/browser/base/content/test/urlbar/browser_bug623155.js
+++ b/browser/base/content/test/urlbar/browser_bug623155.js
@@ -53,23 +53,17 @@ function test() {
   gBrowser.getBrowserForTab(gNewTab)
           .webProgress
           .addProgressListener(gWebProgressListener,
                                Ci.nsIWebProgress
                                  .NOTIFY_LOCATION);
 }
 
 var gWebProgressListener = {
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_NOINTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]),
 
   // ---------------------------------------------------------------------------
   // NOTIFY_LOCATION mode should work fine without these methods.
   //
   // onStateChange: function() {},
   // onStatusChange: function() {},
   // onProgressChange: function() {},
   // onSecurityChange: function() {},
--- a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -84,22 +84,17 @@ function getCacheStorage(where, lci, app
 
 function OpenCacheEntry(key, where, flags, lci) {
   return new Promise(resolve => {
     key = createURI(key);
     function CacheListener() { }
     CacheListener.prototype = {
       _appCache: null,
 
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsICacheEntryOpenCallback) ||
-            iid.equals(Ci.nsISupports))
-          return this;
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      },
+      QueryInterface: ChromeUtils.generateQI(["nsICacheEntryOpenCallback"]),
 
       onCacheEntryCheck(entry, appCache) {
         return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
       },
 
       onCacheEntryAvailable(entry, isnew, appCache, status) {
         resolve();
       },
--- a/browser/components/originattributes/test/browser/browser_cache.js
+++ b/browser/components/originattributes/test/browser/browser_cache.js
@@ -42,22 +42,17 @@ function cacheDataForContext(loadContext
       onCacheStorageInfo(num, consumption) {},
       onCacheEntryInfo(uri, idEnhance) {
         cacheEntries.push({ uri,
                             idEnhance });
       },
       onCacheEntryVisitCompleted() {
         resolve(cacheEntries);
       },
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsICacheStorageVisitor))
-          return this;
-
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      }
+      QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"])
     };
     // Visiting the disk cache also visits memory storage so we do not
     // need to use Services.cache2.memoryCacheStorage() here.
     let storage = Services.cache2.diskCacheStorage(loadContextInfo, false);
     storage.asyncVisitStorage(cacheVisitor, true);
   });
 }
 
--- a/browser/components/originattributes/test/browser/browser_sanitize.js
+++ b/browser/components/originattributes/test/browser/browser_sanitize.js
@@ -20,22 +20,17 @@ function cacheDataForContext(loadContext
     let cacheVisitor = {
       onCacheStorageInfo(num, consumption) {},
       onCacheEntryInfo(uri, idEnhance) {
         cachedURIs.push(uri.asciiSpec);
       },
       onCacheEntryVisitCompleted() {
         resolve(cachedURIs);
       },
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsICacheStorageVisitor))
-          return this;
-
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      }
+      QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"])
     };
     // Visiting the disk cache also visits memory storage so we do not
     // need to use Services.cache2.memoryCacheStorage() here.
     let storage = Services.cache2.diskCacheStorage(loadContextInfo, false);
     storage.asyncVisitStorage(cacheVisitor, true);
   });
 }
 
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -56,17 +56,17 @@ PlacesTreeView.prototype = {
   __xulStore: null,
   get _xulStore() {
     if (!this.__xulStore) {
       this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
     }
     return this.__xulStore;
   },
 
-  QueryInterface: XPCOMUtils.generateQI(PTV_interfaces),
+  QueryInterface: ChromeUtils.generateQI(PTV_interfaces),
 
   // Bug 761494:
   // ----------
   // Some addons use methods from nsINavHistoryResultObserver and
   // nsINavHistoryResultTreeViewer, without QIing to these interfaces first.
   // That's not a problem when the view is retrieved through the
   // <tree>.view getter (which returns the wrappedJSObject of this object),
   // it raises an issue when the view retrieved through the treeBoxObject.view
--- a/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
+++ b/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
@@ -3,16 +3,18 @@
 // docs: http://sinonjs.org/releases/v2.3.2/
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
 // ================================================
 
 ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
 
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 add_task(async function test_no_result_node() {
   let functionSpy = sinon.stub().returns(Promise.resolve());
 
   await PlacesUIUtils.batchUpdatesForNode(null, 1, functionSpy);
 
   Assert.ok(functionSpy.calledOnce,
     "Passing a null result node should still call the wrapped function");
 });
--- a/browser/components/uitour/test/browser_UITour_modalDialog.js
+++ b/browser/components/uitour/test/browser_UITour_modalDialog.js
@@ -1,10 +1,12 @@
 "use strict";
 
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 var handleDialog;
 
 // Modified from toolkit/components/passwordmgr/test/prompt_common.js
 var didDialog;
 
--- a/browser/modules/test/browser/browser_ProcessHangNotifications.js
+++ b/browser/modules/test/browser/browser_ProcessHangNotifications.js
@@ -74,22 +74,17 @@ TestHangReport.prototype = {
   get addonId() {
     return this._addonId;
   },
 
   get hangType() {
     return this._hangType;
   },
 
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIHangReport) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_NOINTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIHangReport"]),
 
   userCanceled() {
     this._resolver(TEST_ACTION_CANCELLED);
   },
 
   terminateScript() {
     this._resolver(TEST_ACTION_TERMSCRIPT);
   },
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -2,17 +2,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 {Ci, Cu, CC} = require("chrome");
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                                "devtools/shared/webconsole/network-helper");
 loader.lazyGetter(this, "debugJsModules", function() {
   let {AppConstants} = require("resource://gre/modules/AppConstants.jsm");
   return !!(AppConstants.DEBUG_JS_MODULES);
 });
@@ -33,17 +33,17 @@ loader.lazyGetter(this, "jsonViewStrings
  * and converts it into a JSON Viewer application that allows simple
  * JSON inspection.
  *
  * Inspired by JSON View: https://github.com/bhollis/jsonview/
  */
 function Converter() {}
 
 Converter.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIStreamConverter,
     Ci.nsIStreamListener,
     Ci.nsIRequestObserver
   ]),
 
   get wrappedJSObject() {
     return this;
   },
--- a/devtools/client/jsonview/converter-observer.js
+++ b/devtools/client/jsonview/converter-observer.js
@@ -48,17 +48,17 @@ const CONTENT_SNIFFER_CATEGORY = "net-co
  * that represents the JSON through a viewer interface.
  *
  * This is done in the .js file rather than a .jsm to avoid creating
  * a compartment at startup when no JSON is being viewed.
  */
 function JsonViewSniffer() {}
 
 JsonViewSniffer.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentSniffer]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIContentSniffer]),
 
   get wrappedJSObject() {
     return this;
   },
 
   isTopLevelLoad: function(request) {
     let loadInfo = request.loadInfo;
     if (loadInfo && loadInfo.isTopLevelLoad) {
--- a/devtools/client/responsive.html/browser/web-navigation.js
+++ b/devtools/client/responsive.html/browser/web-navigation.js
@@ -1,16 +1,16 @@
 /* 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 { Ci, Cu, Cr } = require("chrome");
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const { Utils } = require("resource://gre/modules/sessionstore/Utils.jsm");
 
 function readInputStreamToString(stream) {
   return NetUtil.readInputStreamToString(stream, stream.available());
 }
 
@@ -28,17 +28,17 @@ function readInputStreamToString(stream)
  * perform all actions.
  */
 function BrowserElementWebNavigation(browser) {
   this._browser = browser;
 }
 
 BrowserElementWebNavigation.prototype = {
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIWebNavigation
   ]),
 
   get _mm() {
     return this._browser.frameLoader.messageManager;
   },
 
   canGoBack: false,
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -60,17 +60,17 @@ registerCleanupFunction(function() {
 
 // Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 /**
  * Watch console messages for failed propType definitions in React components.
  */
 const ConsoleObserver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   observe: function(subject) {
     let message = subject.wrappedJSObject.arguments[0];
 
     if (message && /Failed propType/.test(message.toString())) {
       ok(false, message);
     }
   }
--- a/devtools/server/actors/csscoverage.js
+++ b/devtools/server/actors/csscoverage.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
 
 const InspectorUtils = require("InspectorUtils");
 const Services = require("Services");
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 
 const protocol = require("devtools/shared/protocol");
 const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");
 
 loader.lazyRequireGetter(this, "prettifyCSS", "devtools/shared/inspector/css-logic", true);
 
 const MAX_UNUSED_RULES = 10000;
 
@@ -97,18 +97,18 @@ var CSSUsageActor = protocol.ActorClassW
 
     this._isOneShot = false;
     this._visitedPages = new Set();
     this._knownRules = new Map();
     this._running = true;
     this._tooManyUnused = false;
 
     this._progressListener = {
-      QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener,
-                                              Ci.nsISupportsWeakReference ]),
+      QueryInterface: ChromeUtils.generateQI([ Ci.nsIWebProgressListener,
+                                               Ci.nsISupportsWeakReference ]),
 
       onStateChange: (progress, request, flags, status) => {
         let isStop = flags & Ci.nsIWebProgressListener.STATE_STOP;
         let isWindow = flags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
 
         if (isStop && isWindow) {
           this._onTabLoad(progress.DOMWindow.document);
         }
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -1,17 +1,17 @@
 /* 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 { Ci, Cu } = require("chrome");
 
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
 const protocol = require("devtools/shared/protocol");
 const Services = require("Services");
 const { highlighterSpec, customHighlighterSpec } = require("devtools/shared/specs/highlighters");
 
 loader.lazyRequireGetter(this, "isWindowIncluded", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isXUL", "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "SimpleOutlineHighlighter", "devtools/server/actors/highlighters/simple-outline", true);
@@ -574,17 +574,17 @@ HighlighterEnvironment.prototype = {
 
   initFromWindow: function(win) {
     this._win = win;
 
     // We need a progress listener to know when the window will navigate/has
     // navigated.
     let self = this;
     this.listener = {
-      QueryInterface: XPCOMUtils.generateQI([
+      QueryInterface: ChromeUtils.generateQI([
         Ci.nsIWebProgressListener,
         Ci.nsISupportsWeakReference
       ]),
 
       onStateChange: function(progress, request, flag) {
         let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
         let isStop = flag & Ci.nsIWebProgressListener.STATE_STOP;
         let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
--- a/devtools/server/actors/reflow.js
+++ b/devtools/server/actors/reflow.js
@@ -20,17 +20,17 @@
  *   need a to observe something on the tabActor's windows.
  *
  * - Dedicated observers: There's only one of them for now: ReflowObserver which
  *   listens to reflow events via the docshell,
  *   These dedicated classes are used by the LayoutChangesObserver.
  */
 
 const {Ci} = require("chrome");
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const protocol = require("devtools/shared/protocol");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {reflowSpec} = require("devtools/shared/specs/reflow");
 
 /**
  * The reflow actor tracks reflows and emits events about them.
  */
 exports.ReflowActor = protocol.ActorClassWithSpec(reflowSpec, {
@@ -455,17 +455,17 @@ class ReflowObserver extends Observable 
     this.notifyCallback(start, end, false);
   }
 
   reflowInterruptible(start, end) {
     this.notifyCallback(start, end, true);
   }
 }
 
-ReflowObserver.prototype.QueryInterface = XPCOMUtils
+ReflowObserver.prototype.QueryInterface = ChromeUtils
   .generateQI([Ci.nsIReflowObserver, Ci.nsISupportsWeakReference]);
 
 /**
  * Reports window resize events on the tabActor's windows.
  * @extends Observable
  * @param {TabActor} tabActor
  * @param {Function} callback Executed everytime a resize occurs
  */
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -9,17 +9,17 @@
 /* global XPCNativeWrapper */
 
 // For performance matters, this file should only be loaded in the targeted
 // document process. For example, it shouldn't be evaluated in the parent
 // process until we try to debug a document living in the parent process.
 
 var { Ci, Cu, Cr, Cc } = require("chrome");
 var Services = require("Services");
-var { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 var {
   ActorPool, createExtraActors, appendExtraActors
 } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
@@ -1500,17 +1500,17 @@ function DebuggerProgressListener(tabAct
   // so that we can discriminate windows we care about when observing
   // inner-window-destroyed events. Bug 1016952 would remove the need for this.
   this._knownWindowIDs = new Map();
 
   this._watchedDocShells = new WeakSet();
 }
 
 DebuggerProgressListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
   ]),
 
   destroy() {
     Services.obs.removeObserver(this, "inner-window-destroyed");
     this._knownWindowIDs.clear();
     this._knownWindowIDs = null;
--- a/devtools/server/actors/webconsole/content-process-forward.js
+++ b/devtools/server/actors/webconsole/content-process-forward.js
@@ -1,16 +1,15 @@
 /* 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
-const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
 ChromeUtils.defineModuleGetter(this, "E10SUtils",
                                "resource://gre/modules/E10SUtils.jsm");
 
 /*
  * The message manager has an upper limit on message sizes that it can
  * reliably forward to the parent so we limit the size of console log event
  * messages that we forward here. The web console is local and receives the
@@ -32,18 +31,18 @@ const MSG_MGR_CONSOLE_VAR_SIZE = 8;
 const MSG_MGR_CONSOLE_INFO_MAX = 1024;
 
 function ContentProcessForward() {
   Services.obs.addObserver(this, "console-api-log-event");
   Services.obs.addObserver(this, "xpcom-shutdown");
   Services.cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
 }
 ContentProcessForward.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   receiveMessage(message) {
     if (message.name == "DevTools:StopForwardingContentProcessMessage") {
       this.uninit();
     }
   },
 
   observe(subject, topic, data) {
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners.js
@@ -2,17 +2,17 @@
  * 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 {Cc, Ci, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const Services = require("Services");
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
 
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 // Process script used to forward console calls from content processes to parent process
 const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
 
 // The page errors listener
@@ -33,17 +33,17 @@ const CONTENT_PROCESS_SCRIPT = "resource
 function ConsoleServiceListener(window, listener) {
   this.window = window;
   this.listener = listener;
 }
 exports.ConsoleServiceListener = ConsoleServiceListener;
 
 ConsoleServiceListener.prototype =
 {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
 
   /**
    * The content window for which we listen to page errors.
    * @type nsIDOMWindow
    */
   window: null,
 
   /**
@@ -198,17 +198,17 @@ function ConsoleAPIListener(window, owne
   this.window = window;
   this.owner = owner;
   this.addonId = addonId;
 }
 exports.ConsoleAPIListener = ConsoleAPIListener;
 
 ConsoleAPIListener.prototype =
 {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   /**
    * The content window for which we listen to window.console API calls.
    * @type nsIDOMWindow
    */
   window: null,
 
   /**
@@ -385,18 +385,18 @@ function ConsoleReflowListener(window, l
   this.listener = listener;
   this.docshell.addWeakReflowObserver(this);
 }
 
 exports.ConsoleReflowListener = ConsoleReflowListener;
 
 ConsoleReflowListener.prototype =
 {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
+                                          Ci.nsISupportsWeakReference]),
   docshell: null,
   listener: null,
 
   /**
    * Forward reflow event to listener.
    *
    * @param DOMHighResTimeStamp start
    * @param DOMHighResTimeStamp end
--- a/devtools/server/actors/webextension-inspected-window.js
+++ b/devtools/server/actors/webextension-inspected-window.js
@@ -5,28 +5,25 @@
 "use strict";
 
 const protocol = require("devtools/shared/protocol");
 
 const {Cc, Ci, Cu, Cr} = require("chrome");
 
 const {DebuggerServer} = require("devtools/server/main");
 const Services = require("Services");
+const ChromeUtils = require("ChromeUtils");
 
 loader.lazyGetter(this, "NodeActor", () => require("devtools/server/actors/inspector/node").NodeActor, true);
 
 const {
-  XPCOMUtils,
-} = require("resource://gre/modules/XPCOMUtils.jsm");
-
-const {
   webExtensionInspectedWindowSpec,
 } = require("devtools/shared/specs/webextension-inspected-window");
 
-const {WebExtensionPolicy} = Cu.getGlobalForObject(XPCOMUtils);
+const {WebExtensionPolicy} = Cu.getGlobalForObject(require("resource://gre/modules/XPCOMUtils.jsm"));
 
 // A weak set of the documents for which a warning message has been
 // already logged (so that we don't keep emitting the same warning if an
 // extension keeps calling the devtools.inspectedWindow.eval API method
 // when it fails to retrieve a result, but we do log the warning message
 // if the user reloads the window):
 //
 // WeakSet<Document>
@@ -107,18 +104,18 @@ function CustomizedReload(params) {
   this.ignoreCache = params.ignoreCache;
   this.injectedScript = params.injectedScript;
   this.userAgent = params.userAgent;
 
   this.customizedReloadWindows = new WeakSet();
 }
 
 CustomizedReload.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                          Ci.nsISupportsWeakReference]),
   get window() {
     return this.docShell.DOMWindow;
   },
 
   get webNavigation() {
     return this.docShell
                .QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIWebNavigation);
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -1,15 +1,16 @@
 /* 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 { Ci } = require("chrome");
+const ChromeUtils = require("ChromeUtils");
 const { DebuggerServer } = require("devtools/server/main");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const protocol = require("devtools/shared/protocol");
 const {
   workerSpec,
   pushSubscriptionSpec,
   serviceWorkerRegistrationSpec,
@@ -375,17 +376,17 @@ protocol.ActorClassWithSpec(serviceWorke
 
   unregister() {
     let { principal, scope } = this._registration;
     let unregisterCallback = {
       unregisterSucceeded: function() {},
       unregisterFailed: function() {
         console.error("Failed to unregister the service worker for " + scope);
       },
-      QueryInterface: XPCOMUtils.generateQI(
+      QueryInterface: ChromeUtils.generateQI(
         [Ci.nsIServiceWorkerUnregisterCallback])
     };
     swm.propagateUnregister(principal, unregisterCallback, scope);
 
     return { type: "unregistered" };
   },
 
   getPushSubscription() {
--- a/devtools/server/tests/unit/test_layout-reflows-observer.js
+++ b/devtools/server/tests/unit/test_layout-reflows-observer.js
@@ -1,15 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test the LayoutChangesObserver
 
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 var {
   getLayoutChangesObserver,
   releaseLayoutChangesObserver,
   LayoutChangesObserver
 } = require("devtools/server/actors/reflow");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 // Override set/clearTimeout on LayoutChangesObserver to avoid depending on
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -2,16 +2,17 @@
 /* vim: set ft= javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cc, Ci, Cm, Cu, Cr, components} = require("chrome");
+const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "flags",
@@ -112,17 +113,17 @@ const SINK_CONTRACT_ID = "@mozilla.org/n
 const SINK_CATEGORY_NAME = "net-channel-event-sinks";
 
 function ChannelEventSink() {
   this.wrappedJSObject = this;
   this.collectors = new Set();
 }
 
 ChannelEventSink.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIChannelEventSink]),
 
   registerCollector(collector) {
     this.collectors.add(collector);
   },
 
   unregisterCollector(collector) {
     this.collectors.delete(collector);
 
@@ -280,18 +281,18 @@ function NetworkResponseListener(owner, 
   // See bug 1309523.
   let channel = this.httpActivity.channel;
   this._wrappedNotificationCallbacks = channel.notificationCallbacks;
   channel.notificationCallbacks = this;
 }
 
 NetworkResponseListener.prototype = {
   QueryInterface:
-    XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInputStreamCallback,
-                           Ci.nsIRequestObserver, Ci.nsIInterfaceRequestor]),
+    ChromeUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInputStreamCallback,
+                            Ci.nsIRequestObserver, Ci.nsIInterfaceRequestor]),
 
   // nsIInterfaceRequestor implementation
 
   /**
    * This object implements nsIProgressEventSink, but also needs to forward
    * interface requests to the notification callbacks of other objects.
    */
   getInterface(iid) {
@@ -2094,18 +2095,18 @@ ConsoleProgressListener.prototype = {
    * Tells if the console progress listener is initialized or not.
    * @private
    * @type boolean
    */
   _initialized: false,
 
   _webProgress: null,
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                          Ci.nsISupportsWeakReference]),
 
   /**
    * Initialize the ConsoleProgressListener.
    * @private
    */
   _init: function() {
     if (this._initialized) {
       return;
--- a/devtools/shared/webconsole/test/unit/test_security-info-parser.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-parser.js
@@ -1,17 +1,16 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that NetworkHelper.parseSecurityInfo returns correctly formatted object.
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return require("devtools/shared/webconsole/network-helper");
   },
   configurable: true,
   writeable: false,
   enumerable: true
@@ -29,18 +28,18 @@ const MockCertificate = {
   sha1Fingerprint: "qwertyuiop",
   validity: {
     notBeforeLocalDay: "yesterday",
     notAfterLocalDay: "tomorrow",
   }
 };
 
 const MockSecurityInfo = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                         Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
+                                          Ci.nsISSLStatusProvider]),
   securityState: wpl.STATE_IS_SECURE,
   errorCode: 0,
   SSLStatus: {
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
     // TLS_VERSION_1_2
     protocolVersion: 3,
     serverCert: MockCertificate,
   }
--- a/devtools/shared/webconsole/test/unit/test_security-info-state.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-state.js
@@ -2,31 +2,30 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Tests that security info parser gives correct general security state for
 // different cases.
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return require("devtools/shared/webconsole/network-helper");
   },
   configurable: true,
   writeable: false,
   enumerable: true
 });
 
 const wpl = Ci.nsIWebProgressListener;
 const MockSecurityInfo = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                         Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
+                                          Ci.nsISSLStatusProvider]),
   securityState: wpl.STATE_IS_BROKEN,
   errorCode: 0,
   SSLStatus: {
     // nsISSLStatus.TLS_VERSION_1_2
     protocolVersion: 3,
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
   }
 };
--- a/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
@@ -1,33 +1,32 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that NetworkHelper.parseSecurityInfo correctly detects static hpkp pins
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 const Services = require("Services");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return require("devtools/shared/webconsole/network-helper");
   },
   configurable: true,
   writeable: false,
   enumerable: true
 });
 
 const wpl = Ci.nsIWebProgressListener;
 
 const MockSecurityInfo = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                         Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
+                                          Ci.nsISSLStatusProvider]),
   securityState: wpl.STATE_IS_SECURE,
   errorCode: 0,
   SSLStatus: {
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
     // TLS_VERSION_1_2
     protocolVersion: 3,
     serverCert: {
       validity: {}
--- a/devtools/shared/webconsole/test/unit/test_throttle.js
+++ b/devtools/shared/webconsole/test/unit/test_throttle.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const defer = require("devtools/shared/defer");
 const { NetworkThrottleManager } =
       require("devtools/shared/webconsole/throttle");
 const nsIScriptableInputStream = Ci.nsIScriptableInputStream;
 
 function TestStreamListener() {
   this.state = "initial";
--- a/devtools/shared/webconsole/throttle.js
+++ b/devtools/shared/webconsole/throttle.js
@@ -12,17 +12,17 @@ const ArrayBufferInputStream = CC("@mozi
                                   "nsIArrayBufferInputStream");
 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
                              "nsIBinaryInputStream", "setInputStream");
 
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+const ChromeUtils = require("ChromeUtils");
 const {setTimeout} = require("resource://gre/modules/Timer.jsm");
 
 /**
  * Construct a new nsIStreamListener that buffers data and provides a
  * method to notify another listener when data is available.  This is
  * used to throttle network data on a per-channel basis.
  *
  * After construction, @see setOriginalListener must be called on the
@@ -37,17 +37,17 @@ function NetworkThrottleListener(queue) 
   this.pendingException = null;
   this.offset = 0;
   this.responseStarted = false;
   this.activities = {};
 }
 
 NetworkThrottleListener.prototype = {
   QueryInterface:
-    XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInterfaceRequestor]),
+    ChromeUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInterfaceRequestor]),
 
   /**
    * Set the original listener for this object.  The original listener
    * will receive requests from this object when the queue allows data
    * through.
    *
    * @param {nsIStreamListener} originalListener the original listener
    *        for the channel, to which all requests will be sent
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -885,17 +885,17 @@ DevToolsStartup.prototype = {
             "                     Only has an effect when `--jsdebugger` is also supplied.\n" +
             "  --devtools         Open DevTools on initial load.\n" +
             "  --start-debugger-server [ws:][ <port> | <path> ] Start the debugger server on\n" +
             "                     a TCP port or Unix domain socket path. Defaults to TCP port\n" +
             "                     6000. Use WebSocket protocol if ws: prefix is specified.\n",
   /* eslint-disable max-len */
 
   classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsICommandLineHandler]),
 };
 
 /**
  * Singleton object that represents the JSON View in-content tool.
  * It has the same lifetime as the browser.
  */
 const JsonView = {
   initialized: false,
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/.eslintrc.js
@@ -0,0 +1,8 @@
+"use strict";
+
+module.exports = {
+  "rules": {
+    "mozilla/use-chromeutils-generateqi": 0,
+  },
+};
+
--- a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
@@ -12,23 +12,17 @@ FakeSSLStatus.prototype = {
   keyLength: 2048,
   isDomainMismatch: false,
   isNotValidAtThisTime: false,
   isUntrusted: false,
   isExtendedValidation: false,
   getInterface(aIID) {
     return this.QueryInterface(aIID);
   },
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsISSLStatus) ||
-        aIID.equals(Ci.nsISupports)) {
-      return this;
-    }
-    throw new Error(Cr.NS_ERROR_NO_INTERFACE);
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsISSLStatus"]),
 };
 
 function whenNewWindowLoaded(aOptions, aCallback) {
   let win = OpenBrowserWindow(aOptions);
   win.addEventListener("load", function() {
     aCallback(win);
   }, {once: true});
 }
--- a/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
@@ -75,23 +75,17 @@ add_task(async function setup() {
   for (let testCase of TEST_CASES) {
     let cert = null;
     if (testCase.certFilename) {
       cert = await readCertificate(testCase.certFilename, ",,");
     }
     let certTreeItem = {
       hostPort: FAKE_HOST_PORT,
       cert,
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsICertTreeItem)) {
-          return this;
-        }
-
-        throw new Error(Cr.NS_ERROR_NO_INTERFACE);
-      }
+      QueryInterface: ChromeUtils.generateQI(["nsICertTreeItem"])
     };
     gCertArray.push(certTreeItem);
   }
 });
 
 /**
  * Test helper for the below test cases.
  *
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -707,23 +707,17 @@ FakeSSLStatus.prototype = {
   keyLength: 2048,
   isDomainMismatch: false,
   isNotValidAtThisTime: false,
   isUntrusted: false,
   isExtendedValidation: false,
   getInterface(aIID) {
     return this.QueryInterface(aIID);
   },
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsISSLStatus) ||
-        aIID.equals(Ci.nsISupports)) {
-      return this;
-    }
-    throw new Error(Cr.NS_ERROR_NO_INTERFACE);
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsISSLStatus"]),
 };
 
 // Utility functions for adding tests relating to certificate error overrides
 
 // Helper function for add_cert_override_test. Probably doesn't need to be
 // called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
   let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
--- a/security/manager/ssl/tests/unit/test_ocsp_private_caching.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_private_caching.js
@@ -76,22 +76,17 @@ function add_ocsp_necko_cache_test(loadC
                      "expected OCSP request URI should match");
         foundEntry = true;
       },
       onCacheEntryVisitCompleted() {
         Assert.equal(foundEntry, shouldFindEntry,
                      "should only find a cached entry if we're expecting one");
         run_next_test();
       },
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsICacheStorageVisitor)) {
-          return this;
-        }
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      },
+      QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"]),
     };
     Services.cache2.asyncVisitAllStorages(visitor, true);
   });
 
   // Clean up (stop the responder).
   add_test(() => {
     responder.stop(run_next_test);
   });
--- a/services/fxaccounts/tests/xpcshell/test_push_service.js
+++ b/services/fxaccounts/tests/xpcshell/test_push_service.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests for the FxA push service.
 
 /* eslint-disable no-shadow */
+/* eslint-disable mozilla/use-chromeutils-generateqi */
 
 ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 
 let importScope = {};
 Services.scriptloader.loadSubScript("resource://gre/components/FxAccountsPush.js", importScope);
 const FxAccountsPushService = importScope.FxAccountsPushService;
 
--- a/testing/marionette/test/unit/test_cookie.js
+++ b/testing/marionette/test/unit/test_cookie.js
@@ -1,14 +1,16 @@
 /* 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/. */
 
 ChromeUtils.import("chrome://marionette/content/cookie.js");
 
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 cookie.manager = {
   cookies: [],
 
   add(domain, path, name, value, secure, httpOnly, session, expiry, originAttributes) {
     if (name === "fail") {
       throw new Error("An error occurred while adding cookie");
     }
     let newCookie = {
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -1177,23 +1177,17 @@ Tester.prototype = {
         self.currentTest.addResult(new testResult({ name: "Test timed out" }));
         self.currentTest.timedOut = true;
         self.currentTest.scope.__waitTimer = null;
         self.nextTest();
       }, gTimeoutSeconds * 1000]);
     }
   },
 
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIConsoleListener) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"])
 };
 
 /**
  * Represents the result of one test assertion. This is described with a string
  * in traditional logging, and has a "status" and "expected" property used in
  * structured logging. Normally, results are mapped as follows:
  *
  *   pass:    todo:    Added to:    Described as:           Status:  Expected:
--- a/testing/mochitest/server.js
+++ b/testing/mochitest/server.js
@@ -6,16 +6,17 @@
 
 // We expect these to be defined in the global scope by runtest.py.
 /* global __LOCATION__, _PROFILE_PATH, _SERVER_PORT, _SERVER_ADDR, _DISPLAY_RESULTS,
           _TEST_PREFIX */
 // Defined by xpcshell
 /* global quit */
 
 /* import-globals-from ../../netwerk/test/httpserver/httpd.js */
+/* eslint-disable mozilla/use-chromeutils-generateqi */
 
 // Disable automatic network detection, so tests work correctly when
 // not connected to a network.
 // eslint-disable-next-line mozilla/use-services
 var ios = Cc["@mozilla.org/network/io-service;1"]
             .getService(Ci.nsIIOService);
 ios.manageOfflineStatus = false;
 ios.offline = false;
--- a/testing/modules/AppInfo.jsm
+++ b/testing/modules/AppInfo.jsm
@@ -85,17 +85,17 @@ var newAppInfo = function(options = {}) 
     };
     interfaces.push(Ci.nsICrashReporter);
   }
 
   for (let key of Object.keys(extraProps)) {
     appInfo.browserTabsRemoteAutostart = extraProps[key];
   }
 
-  appInfo.QueryInterface = XPCOMUtils.generateQI(interfaces);
+  appInfo.QueryInterface = ChromeUtils.generateQI(interfaces);
 
   return appInfo;
 };
 
 var currentAppInfo = newAppInfo();
 
 /**
  * Obtain a reference to the current object used to define XULAppInfo.
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -128,17 +128,17 @@ try {
 // of the test.
 try {
   let levelNames = {};
   for (let level of ["debug", "info", "warn", "error"]) {
     levelNames[Ci.nsIConsoleMessage[level]] = level;
   }
 
   let listener = {
-    QueryInterface: _XPCOMUtils.generateQI(["nsIConsoleListener"]),
+    QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"]),
     observe(msg) {
       if (typeof info === "function")
         info("CONSOLE_MESSAGE: (" + levelNames[msg.logLevel] + ") " + msg.toString());
     }
   };
   // Don't use _Services.console here as it causes one of the devtools tests
   // to fail, probably due to initializing Services.console too early.
   // eslint-disable-next-line mozilla/use-services
@@ -169,17 +169,17 @@ function _Timer(func, delay) {
   var timer = Cc["@mozilla.org/timer;1"]
                 .createInstance(Ci.nsITimer);
   timer.initWithCallback(this, delay + _timerFuzz, timer.TYPE_ONE_SHOT);
 
   // Keep timer alive until it fires
   _pendingTimers.push(timer);
 }
 _Timer.prototype = {
-  QueryInterface: _XPCOMUtils.generateQI(["nsITimerCallback"]),
+  QueryInterface: ChromeUtils.generateQI(["nsITimerCallback"]),
 
   notify(timer) {
     _pendingTimers.splice(_pendingTimers.indexOf(timer), 1);
 
     // The current nsITimer implementation can undershoot, but even if it
     // couldn't, paranoia is probably a virtue here given the potential for
     // random orange on tinderboxen.
     var end = Date.now();
@@ -275,26 +275,27 @@ var _fakeIdleService = {
       if (aOuter) {
         throw Components.Exception("", Cr.NS_ERROR_NO_AGGREGATION);
       }
       return _fakeIdleService.QueryInterface(aIID);
     },
     lockFactory(aLock) {
       throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
     },
-    QueryInterface: _XPCOMUtils.generateQI(["nsIFactory"]),
+    QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
   },
 
   // nsIIdleService
   get idleTime() {
     return 0;
   },
   addIdleObserver() {},
   removeIdleObserver() {},
 
+  // eslint-disable-next-line mozilla/use-chromeutils-generateqi
   QueryInterface(aIID) {
     // Useful for testing purposes, see test_get_idle.js.
     if (aIID.equals(Ci.nsIFactory)) {
       return this.factory;
     }
     if (aIID.equals(Ci.nsIIdleService) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
@@ -1106,17 +1107,17 @@ function do_get_profile(notifyProfileAft
     getFile(prop, persistent) {
       persistent.value = true;
       if (prop == "ProfD" || prop == "ProfLD" || prop == "ProfDS" ||
           prop == "ProfLDS" || prop == "TmpD") {
         return file.clone();
       }
       return null;
     },
-    QueryInterface: _XPCOMUtils.generateQI(["nsIDirectoryServiceProvider"]),
+    QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
   };
   _Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
            .registerProvider(provider);
 
   try {
     _Services.dirsvc.undefine("TmpD");
   } catch (e) {
     // This throws if the key is not already registered, but that
--- a/toolkit/components/autocomplete/tests/unit/test_378079.js
+++ b/toolkit/components/autocomplete/tests/unit/test_378079.js
@@ -46,33 +46,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 
 /**
  * nsIAutoCompleteResult implementation
  */
 function AutoCompleteResult(aValues, aComments, aStyles) {
@@ -125,23 +113,17 @@ AutoCompleteResult.prototype = {
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"])
 };
 
 
 
 /**
  * nsIAutoCompleteSearch implementation that always returns
  * the same result set.
  */
@@ -167,24 +149,17 @@ AutoCompleteSearch.prototype = {
                         aPreviousResult,
                         aListener) {
     aListener.onSearchResult(this, this._result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   }
 };
 
 
--- a/toolkit/components/autocomplete/tests/unit/test_393191.js
+++ b/toolkit/components/autocomplete/tests/unit/test_393191.js
@@ -45,33 +45,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 
 /**
  * nsIAutoCompleteResult implementation
  */
 function AutoCompleteResult(aValues, aComments, aStyles) {
@@ -124,23 +112,17 @@ AutoCompleteResult.prototype = {
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"])
 };
 
 
 
 /**
  * nsIAutoCompleteSearch implementation that always returns
  * the same result set.
  */
@@ -166,24 +148,17 @@ AutoCompleteSearch.prototype = {
                         aPreviousResult,
                         aListener) {
     aListener.onSearchResult(this, this._result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   }
 };
 
 
--- a/toolkit/components/autocomplete/tests/unit/test_440866.js
+++ b/toolkit/components/autocomplete/tests/unit/test_440866.js
@@ -44,33 +44,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 
 /**
  * nsIAutoCompleteResult implementation
  */
 function AutoCompleteResult(aValues, aComments, aStyles) {
@@ -123,23 +111,17 @@ AutoCompleteResult.prototype = {
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"])
 };
 
 
 
 /**
  * nsIAutoCompleteSearch implementation that always returns
  * the same result set.
  */
@@ -165,24 +147,17 @@ AutoCompleteSearch.prototype = {
                         aPreviousResult,
                         aListener) {
     aListener.onSearchResult(this, this._result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   }
 };
 
 
--- a/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
@@ -39,33 +39,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 
 /**
  * nsIAutoCompleteResult implementation
  */
 function AutoCompleteResult(aValues, aComments, aStyles) {
@@ -112,23 +100,17 @@ AutoCompleteResult.prototype = {
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"])
 };
 
 
 
 /**
  * nsIAutoCompleteSearch implementation that always returns
  * the same result set.
  */
@@ -167,24 +149,17 @@ AutoCompleteSearch.prototype = {
       result.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
     }
     aListener.onSearchResult(this, result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   }
 };
 
 
--- a/toolkit/components/autocomplete/tests/unit/test_previousResult.js
+++ b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
@@ -45,33 +45,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 
 /**
  * nsIAutoCompleteResult implementation
  */
 function AutoCompleteResult(aValues, aComments, aStyles) {
@@ -123,23 +111,17 @@ AutoCompleteResult.prototype = {
 
   getFinalCompleteValueAt(aIndex) {
     return this.getValueAt(aIndex);
   },
 
   removeValueAt(aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteResult))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"])
 };
 
 
 /**
  * nsIAutoCompleteSearch implementation that always returns
  * the same result set.
  */
 function AutoCompleteSearch(aName, aResult) {
@@ -167,24 +149,17 @@ AutoCompleteSearch.prototype = {
                         aListener) {
     this._previousResult = aPreviousResult;
     aListener.onSearchResult(this, this._result);
   },
 
   stopSearch() {},
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIAutoCompleteSearch))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIAutoCompleteSearch"]),
 
   // nsIFactory implementation
   createInstance(outer, iid) {
     return this.QueryInterface(iid);
   }
 };
 
 
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
@@ -13,16 +13,18 @@
 <p id="display"></p>
 
 <div id="content" style="display: none">
   <iframe id="iframe"></iframe>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
 var state, action;
 var pwmgr;
 var proxyLogin;
 var isOk;
 var mozproxy, proxiedHost = "http://mochi.test:8888";
 var proxyChannel;
 var systemPrincipal = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal();
 
--- a/toolkit/components/passwordmgr/test/prompt_common.js
+++ b/toolkit/components/passwordmgr/test/prompt_common.js
@@ -1,14 +1,15 @@
 /**
  * NOTE:
  * This file is currently only being used for tests which haven't been
  * fixed to work with e10s. Favor using the `prompt_common.js` file that
  * is in `toolkit/components/prompts/test/` instead.
  */
+/* eslint-disable mozilla/use-chromeutils-generateqi */
 
 var Ci = SpecialPowers.Ci;
 ok(Ci != null, "Access Ci");
 var Cc = SpecialPowers.Cc;
 ok(Cc != null, "Access Cc");
 
 var didDialog;
 
--- a/toolkit/components/passwordmgr/test/test_prompt_async.html
+++ b/toolkit/components/passwordmgr/test/test_prompt_async.html
@@ -4,16 +4,17 @@
     <meta charset="utf-8">
     <title>Test for Async Auth Prompt</title>
     <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="text/javascript" src="prompt_common.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
     <script class="testbody" type="text/javascript">
+        /* eslint-disable mozilla/use-chromeutils-generateqi */
         SimpleTest.waitForExplicitFinish();
         SimpleTest.requestFlakyTimeout("untriaged");
 
         const { NetUtil } = SpecialPowers.Cu.import("resource://gre/modules/NetUtil.jsm");
 
         SpecialPowers.Services.prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
         // Class monitoring number of open dialog windows
         // It checks there is always open just a single dialog per application
--- a/toolkit/components/places/tests/unit/test_000_frecency.js
+++ b/toolkit/components/places/tests/unit/test_000_frecency.js
@@ -168,33 +168,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 add_task(async function test_frecency() {
   // Disable autoFill for this test.
   Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
   registerCleanupFunction(() => Services.prefs.clearUserPref("browser.urlbar.autoFill"));
   for (let bucket of bucketPrefs) {
     await task_initializeBucket(bucket);
--- a/toolkit/components/places/tests/unit/test_408221.js
+++ b/toolkit/components/places/tests/unit/test_408221.js
@@ -32,33 +32,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 // Get tagging service
 try {
   var tagssvc = Cc["@mozilla.org/browser/tagging-service;1"].
                 getService(Ci.nsITaggingService);
 } catch (ex) {
   do_throw("Could not get tagging service\n");
--- a/toolkit/components/places/tests/unit/test_413784.js
+++ b/toolkit/components/places/tests/unit/test_413784.js
@@ -48,33 +48,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 add_task(async function test_autocomplete_non_english() {
   await PlacesTestUtils.addVisits(url);
 
   var controller = Cc["@mozilla.org/autocomplete/controller;1"].
                    getService(Ci.nsIAutoCompleteController);
 
--- a/toolkit/components/places/tests/unit/test_adaptive_bug527311.js
+++ b/toolkit/components/places/tests/unit/test_adaptive_bug527311.js
@@ -43,34 +43,22 @@ AutoCompleteInput.prototype = {
   onSearchComplete: function ACI_onSearchComplete() {},
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex() {},
     invalidate() {},
 
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   onSearchBegin() {},
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 
 function check_results() {
   return new Promise(resolve => {
     let controller = Cc["@mozilla.org/autocomplete/controller;1"].
                      getService(Ci.nsIAutoCompleteController);
     let input = new AutoCompleteInput(["unifiedcomplete"]);
--- a/toolkit/components/places/tests/unit/test_frecency.js
+++ b/toolkit/components/places/tests/unit/test_frecency.js
@@ -47,33 +47,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 async function ensure_results(uris, searchTerm) {
   await PlacesTestUtils.promiseAsyncUpdates();
   await ensure_results_internal(uris, searchTerm);
 }
 
 async function ensure_results_internal(uris, searchTerm) {
--- a/toolkit/components/places/tests/unit/test_tag_autocomplete_search.js
+++ b/toolkit/components/places/tests/unit/test_tag_autocomplete_search.js
@@ -34,33 +34,21 @@ AutoCompleteInput.prototype = {
 
   popupOpen: false,
 
   popup: {
     setSelectedIndex(aIndex) {},
     invalidate() {},
 
     // nsISupports implementation
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIAutoCompletePopup))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
   },
 
   // nsISupports implementation
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIAutoCompleteInput))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
 };
 
 async function ensure_tag_results(results, searchTerm) {
   var controller = Cc["@mozilla.org/autocomplete/controller;1"].
                    getService(Ci.nsIAutoCompleteController);
 
   // Make an AutoCompleteInput that uses our searches
   // and confirms results on search complete
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -135,23 +135,17 @@ var gInstalledPlugins = [
 
 // A fake plugin host for testing plugin telemetry environment.
 var PluginHost = {
   getPluginTags(countRef) {
     countRef.value = gInstalledPlugins.length;
     return gInstalledPlugins.map(plugin => plugin.pluginTag);
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIPluginHost)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIPluginHost"])
 };
 
 function registerFakePluginHost() {
   MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
 }
 
 var SysInfo = {
   overrides: {},
@@ -171,23 +165,17 @@ var SysInfo = {
   getPropertyAsUint32(name) {
       return this.get(name);
   },
 
   get(name) {
     return this._genuine.get(name);
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIPropertyBag2)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIPropertyBag2"])
 };
 
 function registerFakeSysInfo() {
   MockRegistrar.register("@mozilla.org/system-info;1", SysInfo);
 }
 
 function MockAddonWrapper(aAddon) {
   this.addon = aAddon;
--- a/toolkit/components/url-classifier/nsUrlClassifierListManager.js
+++ b/toolkit/components/url-classifier/nsUrlClassifierListManager.js
@@ -669,25 +669,17 @@ PROT_ListManager.prototype.getBackOffTim
   if (!updateUrl || !this.requestBackoffs_[updateUrl]) {
     return 0;
   }
 
   let delay = this.requestBackoffs_[updateUrl].nextRequestDelay();
   return delay == 0 ? 0 : Date.now() + delay;
 };
 
-PROT_ListManager.prototype.QueryInterface = function(iid) {
-  if (iid.equals(Ci.nsISupports) ||
-      iid.equals(Ci.nsIUrlListManager) ||
-      iid.equals(Ci.nsIObserver) ||
-      iid.equals(Ci.nsITimerCallback))
-    return this;
-
-  throw Cr.NS_ERROR_NO_INTERFACE;
-};
+PROT_ListManager.prototype.QueryInterface = ChromeUtils.generateQI(["nsIUrlListManager", "nsIObserver", "nsITimerCallback"]);
 
 var modScope = this;
 function Init() {
   // Pull the library in.
   var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
               .getService().wrappedJSObject;
   /* global BindToObject, RequestBackoffV4 */
   modScope.BindToObject = jslib.BindToObject;
--- a/toolkit/components/url-classifier/tests/browser/classifierHelper.js
+++ b/toolkit/components/url-classifier/tests/browser/classifierHelper.js
@@ -41,22 +41,17 @@ classifierHelper.waitForInit = function(
     Services.io.newURI(url), {});
 
   return new Promise(function(resolve, reject) {
     Services.obs.addObserver(function() {
       resolve();
     }, "mozentries-update-finished");
 
     let listener = {
-      QueryInterface(iid) {
-        if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-          return this;
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      },
+      QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
 
       handleEvent(value) {
         if (value === table) {
           resolve();
         }
       },
     };
     dbService.lookup(principal, table, listener);
@@ -132,23 +127,17 @@ classifierHelper._update = function(upda
   return (async function() {
     // beginUpdate may fail if there's an existing update in progress
     // retry until success or testcase timeout.
     let success = false;
     while (!success) {
       try {
         await new Promise((resolve, reject) => {
           let listener = {
-            QueryInterface(iid) {
-              if (iid.equals(Ci.nsISupports) ||
-                  iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-                return this;
-
-              throw Cr.NS_ERROR_NO_INTERFACE;
-            },
+            QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
             updateUrlRequested(url) { },
             streamFinished(status) { },
             updateError(errorCode) {
               reject(errorCode);
             },
             updateSuccess(requestedTimeout) {
               resolve();
             }
--- a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
@@ -13,23 +13,17 @@ function setTimeout(callback, delay) {
   timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({ notify: callback },
                            delay,
                            Ci.nsITimer.TYPE_ONE_SHOT);
 }
 
 function doUpdate(update) {
   let listener = {
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-        return this;
-
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    },
+    QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
     updateUrlRequested(url) { },
     streamFinished(status) { },
     updateError(errorCode) {
       sendAsyncMessage("updateError", errorCode);
     },
     updateSuccess(requestedTimeout) {
       sendAsyncMessage("updateSuccess");
     }
@@ -69,22 +63,17 @@ function waitForInit() {
   // This url must sync with the table, url in SafeBrowsing.jsm addMozEntries
   const table = "test-phish-simple";
   const url = "http://itisatrap.org/firefox/its-a-trap.html";
 
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(
     Services.io.newURI(url), {});
 
   let listener = {
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-        return this;
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    },
+    QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
 
     handleEvent(value) {
       if (value === table) {
         sendAsyncMessage("safeBrowsingInited");
       }
     },
   };
   dbService.lookup(principal, table, listener);
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -134,22 +134,17 @@ function buildBareUpdate(chunks, hashSiz
   return buildUpdate({"": chunks}, hashSize);
 }
 
 /**
  * Performs an update of the dbservice manually, bypassing the stream updater
  */
 function doSimpleUpdate(updateText, success, failure) {
   var listener = {
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-        return this;
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    },
+    QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
 
     updateUrlRequested(url) { },
     streamFinished(status) { },
     updateError(errorCode) { failure(errorCode); },
     updateSuccess(requestedTimeout) { success(requestedTimeout); }
   };
 
   dbservice.beginUpdate(listener, allTables);
@@ -159,22 +154,17 @@ function doSimpleUpdate(updateText, succ
   dbservice.finishUpdate();
 }
 
 /**
  * Simulates a failed database update.
  */
 function doErrorUpdate(tables, success, failure) {
   var listener = {
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsISupports) ||
-          iid.equals(Ci.nsIUrlClassifierUpdateObserver))
-        return this;
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    },
+    QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
 
     updateUrlRequested(url) { },
     streamFinished(status) { },
     updateError(errorCode) { success(errorCode); },
     updateSuccess(requestedTimeout) { failure(requestedTimeout); }
   };
 
   dbservice.beginUpdate(listener, tables, null);
@@ -356,22 +346,17 @@ var timerArray = [];
 function Timer(delay, cb) {
   this.cb = cb;
   var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback(this, delay, timer.TYPE_ONE_SHOT);
   timerArray.push(timer);
 }
 
 Timer.prototype = {
-QueryInterface(iid) {
-    if (!iid.equals(Ci.nsISupports) && !iid.equals(Ci.nsITimerCallback)) {
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
-    return this;
-  },
+QueryInterface: ChromeUtils.generateQI(["nsITimerCallback"]),
 notify(timer) {
     this.cb();
   }
 };
 
 // LFSRgenerator is a 32-bit linear feedback shift register random number
 // generator. It is highly predictable and is not intended to be used for
 // cryptography but rather to allow easier debugging than a test that uses
--- a/toolkit/components/url-classifier/tests/unit/test_partial.js
+++ b/toolkit/components/url-classifier/tests/unit/test_partial.js
@@ -6,23 +6,17 @@
 function DummyCompleter() {
   this.fragments = {};
   this.queries = [];
   this.tableName = "test-phish-simple";
 }
 
 DummyCompleter.prototype =
 {
-QueryInterface(iid) {
-  if (!iid.equals(Ci.nsISupports) &&
-      !iid.equals(Ci.nsIUrlClassifierHashCompleter)) {
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
-  return this;
-},
+QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierHashCompleter"]),
 
 complete(partialHash, gethashUrl, tableName, cb) {
   this.queries.push(partialHash);
   var fragments = this.fragments;
   var self = this;
   var doCallback = function() {
       if (self.alwaysFail) {
         cb.completionFinished(Cr.NS_ERROR_FAILURE);
--- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
+++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
@@ -209,23 +209,17 @@ function testUrlInMultipleTables() {
 }
 
 function Observer(callback) {
   this.observe = callback;
 }
 
 Observer.prototype =
 {
-QueryInterface(iid) {
-  if (!iid.equals(Ci.nsISupports) &&
-      !iid.equals(Ci.nsIObserver)) {
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
-  return this;
-}
+QueryInterface: ChromeUtils.generateQI(["nsIObserver"])
 };
 
 // Tests a database reset request.
 function testReset() {
   // The moz-phish-simple table is populated separately from the other update in
   // a separate update request. Therefore it should not be reset when we run the
   // updates later in this function.
   var mozAddUrls = [ "moz-reset.com/a" ];
--- a/toolkit/components/windowcreator/tests/unit/test_wwauthpromptfactory.js
+++ b/toolkit/components/windowcreator/tests/unit/test_wwauthpromptfactory.js
@@ -4,23 +4,17 @@ const tPFCID = Components.ID("{749e62f4-
 const tPFContract = "@mozilla.org/passwordmanager/authpromptfactory;1";
 
 /*
  * TestPromptFactory
  *
  * Implements nsIPromptFactory
  */
 var TestPromptFactory = {
-  QueryInterface: function tPF_qi(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIPromptFactory))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIPromptFactory"]),
 
   createInstance: function tPF_ci(outer, iid) {
     if (outer)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return this.QueryInterface(iid);
   },
 
   lockFactory: function tPF_lockf(lock) {
--- a/toolkit/content/widgets/editor.xml
+++ b/toolkit/content/widgets/editor.xml
@@ -21,24 +21,18 @@
             this.makeEditable(this.editortype, true);
         ]]>
       </constructor>
       <destructor/>
 
       <field name="_editorContentListener">
         <![CDATA[
           ({
-            QueryInterface(iid) {
-              if (iid.equals(Ci.nsIURIContentListener) ||
-                  iid.equals(Ci.nsISupportsWeakReference) ||
-                  iid.equals(Ci.nsISupports))
-              return this;
-
-              throw Cr.NS_ERROR_NO_INTERFACE;
-            },
+            QueryInterface: ChromeUtils.generateQI(["nsIURIContentListener",
+                                                    "nsISupportsWeakReference"]),
             onStartURIOpen(uri) {
               return false;
             },
             doContent(contentType, isContentPreferred, request, contentHandler) {
               return false;
             },
             isPreferred(contentType, desiredContentType) {
               return false;
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -312,24 +312,18 @@
           }
           return this.__prefsvc;
         ]]></getter>
       </property>
 
       <field name="_observer"><![CDATA[({
         _self: this,
 
-        QueryInterface(aIID) {
-          if (aIID.equals(Ci.nsIObserver) ||
-              aIID.equals(Ci.nsISupportsWeakReference) ||
-              aIID.equals(Ci.nsISupports))
-            return this;
-
-          throw Cr.NS_ERROR_NO_INTERFACE;
-        },
+        QueryInterface: ChromeUtils.generateQI(["nsIObserver",
+                                                "nsISupportsWeakReference"]),
 
         observe(aSubject, aTopic, aPrefName) {
           if (aTopic != "nsPref:changed")
             return;
 
           let prefsvc = this._self._prefsvc;
 
           switch (aPrefName) {
--- a/toolkit/crashreporter/test/browser/head.js
+++ b/toolkit/crashreporter/test/browser/head.js
@@ -34,23 +34,17 @@ function make_fake_appdir() {
       // Depending on timing we can get requests for other files.
       // When we threw an exception here, in the world before bug 997440, this got lost
       // because of the arbitrary JSContext being used in XPCWrappedJSClass::CallMethod.
       // After bug 997440 this gets reported to our window and causes the tests to fail.
       // So, we'll just dump out a message to the logs.
       dump("WARNING: make_fake_appdir - fake nsIDirectoryServiceProvider - Unexpected getFile for: '" + prop + "'\n");
       return null;
     },
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
-          iid.equals(Ci.nsISupports)) {
-        return this;
-      }
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"])
   };
   // register our new provider
   Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
                  .registerProvider(_provider);
   // and undefine the old value
   try {
     Services.dirsvc.undefine("UAppData");
   } catch (ex) {} // it's ok if this fails, the value might not be cached yet
--- a/toolkit/crashreporter/test/unit/test_crash_with_memory_report.js
+++ b/toolkit/crashreporter/test/unit/test_crash_with_memory_report.js
@@ -24,23 +24,17 @@ function run_test() {
         getFile(prop, persistent) {
           persistent.value = true;
               if (prop == "ProfD" || prop == "ProfLD" || prop == "ProfDS" ||
               prop == "ProfLDS" || prop == "TmpD") {
             return file.clone();
           }
           throw Cr.NS_ERROR_FAILURE;
         },
-        QueryInterface(iid) {
-          if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
-              iid.equals(Ci.nsISupports)) {
-            return this;
-          }
-          throw Cr.NS_ERROR_NO_INTERFACE;
-        }
+        QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"])
       };
       Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
                      .registerProvider(provider);
 
       crashReporter.saveMemoryReport();
     },
     function(mdump, extra) {
       Assert.equal(extra.ContainsMemoryReport, "1");
--- a/toolkit/modules/Integration.jsm
+++ b/toolkit/modules/Integration.jsm
@@ -136,16 +136,17 @@ var Integration = new Proxy({}, {
 });
 
 /**
  * Individual integration point for which overrides can be registered.
  */
 var IntegrationPoint = function() {
   this._overrideFns = new Set();
   this._combined = {
+    // eslint-disable-next-line mozilla/use-chromeutils-generateqi
     QueryInterface() {
       let ex = new Components.Exception(
                    "Integration objects should not be used with XPCOM because" +
                    " they change when new overrides are registered.",
                    Cr.NS_ERROR_NO_INTERFACE);
       Cu.reportError(ex);
       throw ex;
     },
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -616,24 +616,17 @@ function CertOverrideListener(host, bits
 CertOverrideListener.prototype = {
   host: null,
   bits: null,
 
   getInterface(aIID) {
     return this.QueryInterface(aIID);
   },
 
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIBadCertListener2) ||
-        aIID.equals(Ci.nsIInterfaceRequestor) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Components.Exception("No interface", Cr.NS_ERROR_NO_INTERFACE);
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIBadCertListener2", "nsIInterfaceRequestor"]),
 
   notifyCertProblem(socketInfo, sslStatus, targetHost) {
     var cert = sslStatus.QueryInterface(Ci.nsISSLStatus)
                         .serverCert;
     var cos = Cc["@mozilla.org/security/certoverride;1"].
               getService(Ci.nsICertOverrideService);
     cos.rememberValidityOverride(this.host, -1, cert, this.bits, false);
     return true;
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
@@ -30,23 +30,17 @@ var WindowWatcher = {
         aItem.disable = true;
     });
 
     // run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed");
 
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWindowWatcher)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"])
 };
 
 MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
 
 function load_blocklist(aFile, aCallback) {
   Services.obs.addObserver(function observer() {
     Services.obs.removeObserver(observer, "blocklist-updated");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
@@ -31,23 +31,17 @@ var WindowWatcher = {
         aItem.disable = true;
     });
 
     // run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed");
 
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWindowWatcher)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"])
 };
 
 MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
 
 function load_blocklist(aFile, aCallback) {
   Services.obs.addObserver(function observer() {
     Services.obs.removeObserver(observer, "blocklist-updated");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
@@ -31,23 +31,17 @@ var WindowWatcher = {
         aItem.disable = true;
     });
 
     // run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed");
 
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWindowWatcher)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"])
 };
 
 MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
 
 
 function load_blocklist(aFile, aCallback) {
   Services.obs.addObserver(function observer() {
     Services.obs.removeObserver(observer, "blocklist-updated");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
@@ -513,41 +513,29 @@ var WindowWatcher = {
         aItem.disable = true;
     });
 
     // run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed");
 
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWindowWatcher)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"])
 };
 
 MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
 
 var InstallConfirm = {
   confirm(aWindow, aUrl, aInstalls, aInstallCount) {
     aInstalls.forEach(function(aInstall) {
       aInstall.install();
     });
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.amIWebInstallPrompt)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["amIWebInstallPrompt"])
 };
 
 var InstallConfirmFactory = {
   createInstance: function createInstance(outer, iid) {
     if (outer != null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return InstallConfirm.QueryInterface(iid);
   }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
@@ -39,23 +39,17 @@ testserver.registerDirectory("/data/", d
  * This object is both a factory and an mozISpellCheckingEngine implementation (so, it
  * is de-facto a service). It's also an interface requestor that gives out
  * itself when asked for mozISpellCheckingEngine.
  */
 var HunspellEngine = {
   dictionaryURIs: new Map(),
   listener: null,
 
-  QueryInterface: function hunspell_qi(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.mozISpellCheckingEngine))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory", "mozISpellCheckingEngine"]),
   createInstance: function hunspell_ci(outer, iid) {
     if (outer)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return this.QueryInterface(iid);
   },
   lockFactory: function hunspell_lockf(lock) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
--- a/toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
@@ -27,23 +27,17 @@ var WindowWatcher = {
       if (!aItem.blocked)
         aItem.disable = true;
     });
 
     // run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed");
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWindowWatcher)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"])
 };
 
 MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 function load_blocklist(aFile) {
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -1104,24 +1104,17 @@ var gDownloadingPage = {
         gUpdates.wiz.goTo("errors");
       }
     }
   },
 
   /**
    * See nsISupports.idl
    */
-  QueryInterface(iid) {
-    if (!iid.equals(Ci.nsIRequestObserver) &&
-        !iid.equals(Ci.nsIProgressEventSink) &&
-        !iid.equals(Ci.nsIObserver) &&
-        !iid.equals(Ci.nsISupports))
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    return this;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIRequestObserver", "nsIProgressEventSink", "nsIObserver"])
 };
 
 /**
  * The "There was an error during the update" page.
  */
 var gErrorsPage = {
   /**
    * Initialize
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -3860,17 +3860,17 @@ function createAppInfo(aID, aName, aVers
     appBuildID: "2007010101",
     platformVersion: aPlatformVersion,
     platformBuildID: "2007010101",
     inSafeMode: false,
     logConsoleErrors: true,
     OS: "XPCShell",
     XPCOMABI: "noarch-spidermonkey",
 
-    QueryInterface: XPCOMUtils.generateQI(ifaces)
+    QueryInterface: ChromeUtils.generateQI(ifaces)
   };
 
   const XULAppInfoFactory = {
     createInstance(aOuter, aIID) {
       if (aOuter == null) {
         return XULAppInfo.QueryInterface(aIID);
       }
       throw Cr.NS_ERROR_NO_AGGREGATION;
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -172,16 +172,17 @@ module.exports = {
     "mozilla/import-browser-window-globals": "error",
     "mozilla/import-globals": "error",
     "mozilla/no-compare-against-boolean-literals": "error",
     "mozilla/no-define-cc-etc": "error",
     "mozilla/no-import-into-var-and-global": "error",
     "mozilla/no-useless-parameters": "error",
     "mozilla/no-useless-removeEventListener": "error",
     "mozilla/use-cc-etc": "error",
+    "mozilla/use-chromeutils-generateqi": "error",
     "mozilla/use-chromeutils-import": "error",
     "mozilla/use-default-preference-values": "error",
     "mozilla/use-includes-instead-of-indexOf": "error",
     "mozilla/use-ownerGlobal": "error",
     "mozilla/use-services": "error",
 
     // Always require parenthesis for new calls
     // "new-parens": "error",
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -53,16 +53,17 @@ module.exports = {
     "no-useless-removeEventListener":
       require("../lib/rules/no-useless-removeEventListener"),
     "no-useless-run-test":
       require("../lib/rules/no-useless-run-test"),
     "reject-importGlobalProperties":
       require("../lib/rules/reject-importGlobalProperties"),
     "reject-some-requires": require("../lib/rules/reject-some-requires"),
     "use-cc-etc": require("../lib/rules/use-cc-etc"),
+    "use-chromeutils-generateqi": require("../lib/rules/use-chromeutils-generateqi"),
     "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"),
     "use-includes-instead-of-indexOf": require("../lib/rules/use-includes-instead-of-indexOf"),
     "use-services": require("../lib/rules/use-services"),
     "var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
   }
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/use-chromeutils-generateqi.js
@@ -0,0 +1,97 @@
+/**
+ * @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": function(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}`);
+          }
+        });
+      }
+    };
+  }
+};
+
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.11.0",
+  "version": "0.12.0",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/tests/use-chromeutils-generateqi.js
@@ -0,0 +1,77 @@
+/* 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-generateqi");
+var RuleTester = require("eslint/lib/testers/rule-tester");
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+function callError(message) {
+  return [{message, type: "CallExpression"}];
+}
+function error(message, type) {
+  return [{message, type}];
+}
+
+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 QueryInterface(iid) {
+  /* globals nsIFlug */
+  if (iid.equals(Ci.nsISupports) ||
+      iid.equals(Ci.nsIMeh) ||
+      iid.equals(nsIFlug) ||
+      iid.equals(Ci.amIFoo)) {
+    return this;
+  }
+  throw Cr.NS_ERROR_NO_INTERFACE;
+}
+
+ruleTester.run("use-chromeutils-generateqi", rule, {
+  valid: [
+    `X.prototype.QueryInterface = ChromeUtils.generateQI(["nsIMeh"]);`,
+    `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh"]) }`
+  ],
+  invalid: [
+    {
+      code: `X.prototype.QueryInterface = XPCOMUtils.generateQI(["nsIMeh"]);`,
+      output: `X.prototype.QueryInterface = ChromeUtils.generateQI(["nsIMeh"]);`,
+      errors: callError(MSG_NO_XPCOMUTILS_GENERATEQI)
+    },
+    {
+      code: `X.prototype = { QueryInterface: XPCOMUtils.generateQI(["nsIMeh"]) };`,
+      output: `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh"]) };`,
+      errors: callError(MSG_NO_XPCOMUTILS_GENERATEQI)
+    },
+    {
+      code: `X.prototype = { QueryInterface: ${QueryInterface} };`,
+      output: `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh", "nsIFlug", "amIFoo"]) };`,
+      errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property")
+    },
+    {
+      code: `X.prototype = { ${String(QueryInterface).replace(/^function /, "")} };`,
+      output: `X.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIMeh", "nsIFlug", "amIFoo"]) };`,
+      errors: error(MSG_NO_JS_QUERY_INTERFACE, "Property")
+    },
+    {
+      code: `X.prototype.QueryInterface = ${QueryInterface};`,
+      output: `X.prototype.QueryInterface = ChromeUtils.generateQI(["nsIMeh", "nsIFlug", "amIFoo"]);`,
+      errors: error(MSG_NO_JS_QUERY_INTERFACE, "AssignmentExpression")
+    }
+  ]
+});
+
--- a/xpcom/tests/unit/test_bug1434856.js
+++ b/xpcom/tests/unit/test_bug1434856.js
@@ -4,16 +4,17 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function run_test() {
   let complete = false;
 
   let runnable = {
     internalQI: ChromeUtils.generateQI([Ci.nsIRunnable]),
+    // eslint-disable-next-line mozilla/use-chromeutils-generateqi
     QueryInterface(iid) {
       // Attempt to schedule another runnable.  This simulates a GC/CC
       // being scheduled while executing the JS QI.
       Services.tm.dispatchToMainThread(() => false);
       return this.internalQI(iid);
     },
 
     run() {
--- a/xpcom/tests/unit/test_bug374754.js
+++ b/xpcom/tests/unit/test_bug374754.js
@@ -6,22 +6,17 @@ var testCategory = "bug-test-category";
 var testEntry = "@mozilla.org/bug-test-entry;1";
 
 var testValue = "check validity";
 var result = "";
 var expected = "add remove add remove ";
 var timer;
 
 var observer = {
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
 
   observe(subject, topic, data) {
     if (topic == "timer-callback") {
       Assert.equal(result, expected);
 
       Services.obs.removeObserver(this, addedTopic);
       Services.obs.removeObserver(this, removedTopic);