Merge inbound to mozilla-central. a=merge
authorAndreea Pavel <apavel@mozilla.com>
Sat, 28 Apr 2018 20:30:18 +0300
changeset 472266 08f68e2c892cadc4035ecbfbf3529f32d40f1fd9
parent 472218 807e94988d9a9563504d6b3751562fdbbde13084 (current diff)
parent 472260 5bde59ad4082e94f3deea06808152b3ad0077876 (diff)
child 472267 f35994b437e3b0d9e61f74b34de2cf4cb80ab458
child 472269 4f03582a853b24483df1b8927d4200fe54b7cab9
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
08f68e2c892c / 61.0a1 / 20180428220114 / files
nightly linux64
08f68e2c892c / 61.0a1 / 20180428220114 / files
nightly mac
08f68e2c892c / 61.0a1 / 20180428220114 / files
nightly win32
08f68e2c892c / 61.0a1 / 20180428220114 / files
nightly win64
08f68e2c892c / 61.0a1 / 20180428220114 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/base/content/browser-fullZoom.js
browser/base/content/test/plugins/blocklist_proxy.js
dom/webidl/PopupBoxObject.webidl
layout/xul/PopupBoxObject.cpp
layout/xul/PopupBoxObject.h
layout/xul/crashtests/434458-1.xul
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
modules/libpref/init/all.js
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_severities.js
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -497,12 +497,12 @@ this.ContentControl.prototype = {
     }
   },
 
   cancelAutoMove: function cc_cancelAutoMove() {
     this.window.clearTimeout(this._autoMove);
     this._autoMove = 0;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference,
     Ci.nsIMessageListener
   ])
 };
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -551,20 +551,17 @@ this.EventManager.prototype = {
     }
   },
 
   onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
     let docAcc = Utils.AccService.getAccessibleFor(aWebProgress.DOMWindow.document);
     this.present(Presentation.tabStateChanged(docAcc, "newdoc"));
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISupports,
-                                         Ci.nsIObserver])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference, Ci.nsIObserver])
 };
 
 const AccessibilityEventObserver = {
 
   /**
    * A WeakMap containing [content, EventManager] pairs.
    */
   eventManagers: new WeakMap(),
--- a/accessible/jsat/Traversal.jsm
+++ b/accessible/jsat/Traversal.jsm
@@ -62,17 +62,17 @@ BaseTraversalRule.prototype = {
         if (control && this._explicitMatchRoles.has(control.role)) {
           matchResult = this._matchFunc(control) | Filters.IGNORE_SUBTREE;
         }
       }
 
       return matchResult;
     },
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIAccessibleTraversalRule])
 };
 
 var gSimpleTraversalRoles =
   [Roles.MENUITEM,
    Roles.LINK,
    Roles.PAGETAB,
    Roles.GRAPHIC,
    Roles.STATICTEXT,
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -1052,11 +1052,11 @@ PrefCache.prototype = {
       try {
         this.callback(this.name, this.value, false);
       } catch (x) {
         Logger.logException(x);
       }
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                           Ci.nsISupportsWeakReference])
 };
--- a/accessible/tests/mochitest/pivot.js
+++ b/accessible/tests/mochitest/pivot.js
@@ -29,17 +29,17 @@ var HeadersTraversalRule =
   },
 
   preFilter: PREFILTER_INVISIBLE,
 
   match(aAccessible) {
     return FILTER_MATCH;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+  QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule])
 };
 
 /**
  * Traversal rule for all focusable nodes or leafs.
  */
 var ObjectTraversalRule =
 {
   getMatchRoles(aRules) {
@@ -57,17 +57,17 @@ var ObjectTraversalRule =
       rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
     else if (aAccessible.childCount == 0 &&
              role != ROLE_STATICTEXT && aAccessible.name.trim())
       rv = FILTER_MATCH;
 
     return rv;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+  QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule])
 };
 
 // //////////////////////////////////////////////////////////////////////////////
 // Virtual state invokers and checkers
 
 /**
  * A checker for virtual cursor changed events.
  */
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -296,22 +296,17 @@ appUpdater.prototype =
       // notified with the normal app update user interface so this is safe.
       gAppUpdater.isChecking = false;
       gAppUpdater.selectPanel("noUpdatesFound");
     },
 
     /**
      * See nsISupports.idl
      */
-    QueryInterface(aIID) {
-      if (!aIID.equals(Ci.nsIUpdateCheckListener) &&
-          !aIID.equals(Ci.nsISupports))
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      return this;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIUpdateCheckListener"]),
   },
 
   /**
    * Starts the download of an update mar.
    */
   startDownload() {
     if (!this.update)
       this.update = this.um.activeUpdate;
@@ -422,16 +417,10 @@ appUpdater.prototype =
   onProgress(aRequest, aContext, aProgress, aProgressMax) {
     this.downloadStatus.textContent =
       DownloadUtils.getTransferTotal(aProgress, aProgressMax);
   },
 
   /**
    * See nsISupports.idl
    */
-  QueryInterface(aIID) {
-    if (!aIID.equals(Ci.nsIProgressEventSink) &&
-        !aIID.equals(Ci.nsIRequestObserver) &&
-        !aIID.equals(Ci.nsISupports))
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    return this;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIProgressEventSink", "nsIRequestObserver"]),
 };
--- a/browser/base/content/browser-data-submission-info-bar.js
+++ b/browser/base/content/browser-data-submission-info-bar.js
@@ -113,13 +113,13 @@ var gDataNotificationInfoBar = {
         this._actionTaken = true;
         this._clearPolicyNotification();
         break;
 
       default:
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
   ]),
 };
--- a/browser/base/content/browser-feeds.js
+++ b/browser/base/content/browser-feeds.js
@@ -407,18 +407,18 @@ var FeedHandler = {
                 .createInstance(Ci.nsIProcess);
       p.init(clientApp);
       p.run(false, [aSpec], 1);
     }
   },
 
   // nsISupports
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
 
   init() {
     window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this);
     window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this);
     window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this);
     window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);
 
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -34,20 +34,17 @@ var FullZoom = {
         !Services.prefs.getBoolPref("privacy.resistFingerprinting") &&
         Services.prefs.getBoolPref("browser.zoom.siteSpecific");
     }
     return this._siteSpecificPref;
   },
 
   // nsISupports
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIContentPrefObserver,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsIContentPrefObserver, Ci.nsISupportsWeakReference]),
 
   // Initialization & Destruction
 
   init: function FullZoom_init() {
     gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);
 
     // Register ourselves with the service so we know when our pref changes.
     this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -902,17 +902,17 @@ var PlacesMenuDNDHandler = {
     let popup = event.target.lastChild;
     if (this._loadTimer || popup.state === "showing" || popup.state === "open")
       return;
 
     this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this._loadTimer.initWithCallback(() => {
       this._loadTimer = null;
       popup.setAttribute("autoopened", "true");
-      popup.showPopup(popup);
+      popup.openPopup();
     }, this._springLoadDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
     event.preventDefault();
     event.stopPropagation();
   },
 
   /**
    * Handles dragleave on the <menu> element.
    */
@@ -1744,17 +1744,17 @@ var BookmarkingUI = {
     if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window)
       return;
 
     // The view gets broken by being removed and reinserted. Uninit
     // here so popupshowing will generate a new one:
     this._uninitView();
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsINavBookmarkObserver
   ])
 };
 
 var AutoShowBookmarksToolbar = {
   init() {
     Services.obs.addObserver(this, "autoshow-bookmarks-toolbar");
   },
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -685,13 +685,13 @@ var gSync = {
 
   onSyncDisabled() {
     const toHide = [...document.querySelectorAll(".sync-ui-item")];
     for (const item of toHide) {
       item.hidden = true;
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4595,25 +4595,20 @@ var XULBrowserWindow = {
   // Stored Status, Link and Loading values
   status: "",
   defaultStatus: "",
   overLink: "",
   startTime: 0,
   isBusy: false,
   busyUI: false,
 
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsIWebProgressListener2) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsIXULBrowserWindow) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_NOINTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
+                                          "nsIWebProgressListener2",
+                                          "nsISupportsWeakReference",
+                                          "nsIXULBrowserWindow"]),
 
   get stopCommand() {
     delete this.stopCommand;
     return this.stopCommand = document.getElementById("Browser:Stop");
   },
   get reloadCommand() {
     delete this.reloadCommand;
     return this.reloadCommand = document.getElementById("Browser:Reload");
@@ -5379,17 +5374,17 @@ var TabsProgressListener = {
 
     FullZoom.onLocationChange(aLocationURI, false, aBrowser);
   },
 };
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIBrowserDOMWindow]),
 
   _openURIInNewTab(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
                    aIsExternal, aForceNotRemote = false,
                    aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
                    aOpenerWindow = null, aOpenerBrowser = null,
                    aTriggeringPrincipal = null, aNextTabParentId = 0, aName = "") {
     let win, needToFocusWin;
 
@@ -7735,18 +7730,18 @@ var RestoreLastSessionObserver = {
 
   observe() {
     // The last session can only be restored once so there's
     // no way we need to re-enable our menu item.
     Services.obs.removeObserver(this, "sessionstore-last-session-cleared");
     goSetCommandEnabled("Browser:RestoreLastSession", false);
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference])
 };
 
 function restoreLastSession() {
   SessionStore.restoreLastSession();
 }
 
 /* Observes menus and adjusts their size for better
  * usability when opened via a touch screen. */
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -1519,14 +1519,14 @@ let OfflineApps = {
     if (aTopic == "offline-cache-update-completed") {
       let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
       let uri = cacheUpdate.manifestURI;
       if (uri && this._docManifestSet.has(uri.spec)) {
         sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
       }
     }
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 };
 
 addEventListener("MozApplicationManifest", OfflineApps, false);
 addMessageListener("OfflineApps:StartFetching", OfflineApps);
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -854,20 +854,17 @@ var RefreshBlocker = {
                           .QueryInterface(Ci.nsIRefreshURI);
 
       let URI = Services.io.newURI(data.URI);
 
       refreshURI.forceRefreshURI(URI, null, data.delay, true);
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener2,
-                                         Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener2, Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
 };
 
 RefreshBlocker.init();
 
 var UserContextIdNotifier = {
   init() {
     addEventListener("DOMWindowCreated", this);
   },
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -4569,26 +4569,21 @@ class TabProgressListener {
     this._callProgressListeners("onSecurityChange",
                                 [aWebProgress, aRequest, aState]);
   }
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return this._callProgressListeners("onRefreshAttempted",
                                        [aWebProgress, aURI, aDelay, aSameURI]);
   }
-
-  QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsIWebProgressListener2) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_NOINTERFACE;
-  }
 }
+TabProgressListener.prototype.QueryInterface = ChromeUtils.generateQI(
+  ["nsIWebProgressListener",
+   "nsIWebProgressListener2",
+   "nsISupportsWeakReference"]);
 
 var StatusPanel = {
   get panel() {
     window.addEventListener("resize", this);
 
     delete this.panel;
     return this.panel = document.getElementById("statuspanel");
   },
--- a/browser/base/content/test/about/head.js
+++ b/browser/base/content/test/about/head.js
@@ -114,17 +114,17 @@ function waitForDocLoadAndStopIt(aExpect
           wp.removeProgressListener(progressListener);
 
           let chan = req.QueryInterface(Ci.nsIChannel);
           dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`);
 
           stopContent(contentStopFromProgressListener, chan.originalURI.spec);
         }
       },
-      QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"])
+      QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"])
     };
     wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW);
 
     /**
      * As |this| is undefined and we can't extend |docShell|, adding an unload
      * event handler is the easiest way to ensure the weakly referenced
      * progress listener is kept alive as long as necessary.
      */
--- a/browser/base/content/test/chrome/test_aboutCrashed.xul
+++ b/browser/base/content/test/chrome/test_aboutCrashed.xul
@@ -27,18 +27,18 @@
                             .getInterface(Ci.nsIWebProgress)
                             .removeProgressListener(progressListener,
                                                     Ci.nsIWebProgress.NOTIFY_LOCATION);
 
               resolve();
             }
           },
 
-          QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                                 Ci.nsISupportsWeakReference])
+          QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                                  Ci.nsISupportsWeakReference])
         };
 
         frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebProgress)
                       .addProgressListener(progressListener,
                                            Ci.nsIWebProgress.NOTIFY_LOCATION);
       });
     }
--- a/browser/base/content/test/general/browser_bug356571.js
+++ b/browser/base/content/test/general/browser_bug356571.js
@@ -17,17 +17,17 @@ var fakePromptServiceFactory = {
   createInstance(aOuter, aIid) {
     if (aOuter != null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return promptService.QueryInterface(aIid);
   }
 };
 
 var promptService = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
   alert() {
     didFail = true;
   }
 };
 
 /* FIXME
 Cm.QueryInterface(Ci.nsIComponentRegistrar)
   .registerFactory(Components.ID(kPromptServiceUUID), "Prompt Service",
--- a/browser/base/content/test/general/browser_e10s_about_process.js
+++ b/browser/base/content/test/general/browser_e10s_about_process.js
@@ -41,31 +41,31 @@ AboutModule.prototype = {
     ok(false, "Called getURIFlags for an unknown page " + aURI.spec);
     return 0;
   },
 
   getIndexedDBOriginPostfix(aURI) {
     return null;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule])
 };
 
 var AboutModuleFactory = {
   createInstance(aOuter, aIID) {
     if (aOuter)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return new AboutModule().QueryInterface(aIID);
   },
 
   lockFactory(aLock) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory])
 };
 
 add_task(async function init() {
   let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
   for (let module of TEST_MODULES) {
     registrar.registerFactory(Components.ID(module.id), "",
                               "@mozilla.org/network/protocol/about;1?what=" + module.path,
                               AboutModuleFactory);
--- a/browser/base/content/test/general/browser_fullscreen-window-open.js
+++ b/browser/base/content/test/general/browser_fullscreen-window-open.js
@@ -337,11 +337,10 @@ WindowListener.prototype = {
       } else {
         domwindow.close();
         executeSoon(this.callBack_onFinalize);
       }
     };
     domwindow.addEventListener("load", onLoad, true);
   },
   onCloseWindow(aXULWindow) {},
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowMediatorListener,
-                                         Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowMediatorListener]),
 };
--- a/browser/base/content/test/general/browser_homeDrop.js
+++ b/browser/base/content/test/general/browser_homeDrop.js
@@ -24,17 +24,17 @@ add_task(async function() {
     EventUtils.synthesizeDrop(dragSrcElement, homeButton, dragData, "copy", window);
 
     let setHomepageDialog = await setHomepageDialogPromise;
     ok(true, "dialog appeared in response to home button drop");
     await BrowserTestUtils.waitForEvent(setHomepageDialog, "load", false);
 
     let setHomepagePromise = new Promise(function(resolve) {
       let observer = {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
         observe(subject, topic, data) {
           is(topic, "nsPref:changed", "observed correct topic");
           is(data, HOMEPAGE_PREF, "observed correct data");
           let modified = Services.prefs.getStringPref(HOMEPAGE_PREF);
           is(modified, homepage, "homepage is set correctly");
           Services.prefs.removeObserver(HOMEPAGE_PREF, observer);
 
           Services.prefs.setStringPref(HOMEPAGE_PREF, "about:mozilla;");
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -542,17 +542,17 @@ function promiseOnBookmarkItemAdded(aExp
         }
       },
       onBeginUpdateBatch() {},
       onEndUpdateBatch() {},
       onItemRemoved() {},
       onItemChanged() {},
       onItemVisited() {},
       onItemMoved() {},
-      QueryInterface: XPCOMUtils.generateQI([
+      QueryInterface: ChromeUtils.generateQI([
         Ci.nsINavBookmarkObserver,
       ])
     };
     info("Waiting for a bookmark to be added");
     PlacesUtils.bookmarks.addObserver(bookmarksObserver);
   });
 }
 
--- a/browser/base/content/test/performance/browser_appmenu.js
+++ b/browser/base/content/test/performance/browser_appmenu.js
@@ -11,46 +11,36 @@ let gCUITestUtils = new CustomizableUITe
  * reflows to the whitelist, you should be modifying your code to avoid the reflow.
  *
  * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
  * for tips on how to do that.
  */
 const EXPECTED_APPMENU_OPEN_REFLOWS = [
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "openPopup/this._openPopupPromise<@resource:///modules/PanelMultiView.jsm",
     ],
   },
 
   {
     stack: [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
       "adjustArrowPosition@chrome://global/content/bindings/popup.xml",
       "onxblpopuppositioned@chrome://global/content/bindings/popup.xml",
     ],
 
     maxCount: 3, // This number should only ever go down - never up.
   },
 
   {
     stack: [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
-      "_calculateMaxHeight@resource:///modules/PanelMultiView.jsm",
-      "handleEvent@resource:///modules/PanelMultiView.jsm",
-    ],
-  },
-
-  {
-    stack: [
       "_calculateMaxHeight@resource:///modules/PanelMultiView.jsm",
       "handleEvent@resource:///modules/PanelMultiView.jsm",
     ],
 
-    maxCount: 6, // This number should only ever go down - never up.
+    maxCount: 7, // This number should only ever go down - never up.
   },
 ];
 
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
     "anonid", "textbox-input-box").getBoundingClientRect();
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search.js
@@ -18,17 +18,16 @@ requestLongerTimeout(5);
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
 
   {
@@ -69,17 +68,16 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
     maxCount: 6, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
--- a/browser/base/content/test/performance/browser_urlbar_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_search.js
@@ -18,17 +18,16 @@ requestLongerTimeout(5);
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
 
   {
@@ -69,17 +68,16 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
     maxCount: 6, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
@@ -111,17 +109,16 @@ const EXPECTED_REFLOWS_SECOND_OPEN = [
       "invalidate@chrome://global/content/bindings/autocomplete.xml"
     ],
     maxCount: 24, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -49,18 +49,18 @@ async function recordReflows(testPromise
       dirtyFrame(win);
     },
 
     reflowInterruptible(start, end) {
       // Interruptible reflows are the reflows caused by the refresh
       // driver ticking. These are fine.
     },
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
-                                           Ci.nsISupportsWeakReference])
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
+                                            Ci.nsISupportsWeakReference])
   };
 
   let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShell);
   docShell.addWeakReflowObserver(observer);
 
   let dirtyFrameFn = event => {
--- a/browser/base/content/test/plugins/blocklist_proxy.js
+++ b/browser/base/content/test/plugins/blocklist_proxy.js
@@ -10,19 +10,19 @@ ChromeUtils.import("resource://gre/modul
 SimpleTest.requestFlakyTimeout("Need to simulate blocklist calls actually taking non-0 time to return");
 
 /*
  * A lightweight blocklist proxy for the testing purposes.
  */
 var BlocklistProxy = {
   _uuid: null,
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIBlocklistService,
-                                         Ci.nsITimerCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsIBlocklistService,
+                                          Ci.nsITimerCallback]),
 
   init() {
     if (!this._uuid) {
       this._uuid =
         Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
                                            .generateUUID();
       Cm.nsIComponentRegistrar.registerFactory(this._uuid, "",
                                                "@mozilla.org/extensions/blocklist;1",
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
@@ -35,17 +35,17 @@ function startNewTabTestCase(aTestNumber
 
       aContextMenu.addEventListener("popuphidden", function() {
         firstContext.doCommand();
       }, {once: true});
 
       aContextMenu.hidePopup();
     }, {once: true});
 
-    menupopup.showPopup();
+    menupopup.openPopup();
   });
 }
 
 function test() {
   waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv(
     {set: [["privacy.userContext.enabled", true]]},
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
@@ -31,17 +31,17 @@ function startNewTabTestCase(aTestNumber
 
       aContextMenu.addEventListener("popuphidden", function() {
         firstContext.doCommand();
       }, {once: true});
 
       aContextMenu.hidePopup();
     }, {once: true});
 
-    menupopup.showPopup();
+    menupopup.openPopup();
   });
 }
 
 function test() {
   waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv(
     {set: [["privacy.userContext.enabled", true]]},
--- a/browser/base/content/test/sync/browser_contextmenu_sendpage.js
+++ b/browser/base/content/test/sync/browser_contextmenu_sendpage.js
@@ -232,17 +232,17 @@ async function openContentContextMenu(se
       centered: true
     },
     gBrowser.selectedBrowser);
   await awaitPopupShown;
 
   if (openSubmenuId) {
     const menuPopup = document.getElementById(openSubmenuId).menupopup;
     const menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
-    menuPopup.showPopup();
+    menuPopup.openPopup();
     await menuPopupPromise;
   }
 }
 
 async function hideContentContextMenu() {
   const contextMenu = document.getElementById("contentAreaContextMenu");
   const awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
   contextMenu.hidePopup();
--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
+++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
@@ -199,17 +199,17 @@ function checkAutocompleteResults(aExpec
         ok(false, "'" + entry + "' should be found in autocomplete");
       }
 
       executeSoon(aCallback);
     },
     setSelectedIndex() {},
     get searchCount() { return this.searches.length; },
     getSearchAt(aIndex) { return this.searches[aIndex]; },
-    QueryInterface: XPCOMUtils.generateQI([
+    QueryInterface: ChromeUtils.generateQI([
       Ci.nsIAutoCompleteInput,
       Ci.nsIAutoCompletePopup,
     ])
   };
 
   info("Searching open pages.");
   gController.startSearch(RESTRICT_TOKEN_OPENPAGE);
 }
--- a/browser/base/content/test/urlbar/browser_urlbar_search_no_speculative_connect_with_client_cert.js
+++ b/browser/base/content/test/urlbar/browser_urlbar_search_no_speculative_connect_with_client_cert.js
@@ -27,17 +27,17 @@ const clientAuthDialogs = {
     ok(expectingChooseCertificate,
        `${expectingChooseCertificate ? "" : "not "}expecting chooseCertificate to be called`);
     is(certList.length, 1, "should have only one client certificate available");
     selectedIndex.value = 0;
     chooseCertificateCalled = true;
     return true;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIClientAuthDialogs]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogs]),
 };
 
 function startServer(cert) {
   let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"]
                     .createInstance(Ci.nsITLSServerSocket);
   tlsServer.init(-1, true, -1);
   tlsServer.serverCert = cert;
 
--- a/browser/base/content/test/urlbar/head.js
+++ b/browser/base/content/test/urlbar/head.js
@@ -57,17 +57,17 @@ function waitForDocLoadAndStopIt(aExpect
           wp.removeProgressListener(progressListener);
 
           let chan = req.QueryInterface(Ci.nsIChannel);
           dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`);
 
           stopContent(contentStopFromProgressListener, chan.originalURI.spec);
         }
       },
-      QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"])
+      QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"])
     };
     wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW);
 
     /**
      * As |this| is undefined and we can't extend |docShell|, adding an unload
      * event handler is the easiest way to ensure the weakly referenced
      * progress listener is kept alive as long as necessary.
      */
--- a/browser/base/content/web-panels.js
+++ b/browser/base/content/web-panels.js
@@ -42,23 +42,17 @@ var panelProgressListener = {
     },
 
     onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     },
 
     onSecurityChange(aWebProgress, aRequest, aState) {
     },
 
-    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"]),
 };
 
 var gLoadFired = false;
 function loadWebPanel(aURI) {
     var panelBrowser = getPanelBrowser();
     if (gLoadFired) {
         panelBrowser.webNavigation
                     .loadURI(aURI, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
--- a/browser/components/about/nsAboutCapabilities.js
+++ b/browser/components/about/nsAboutCapabilities.js
@@ -62,13 +62,12 @@ nsAboutCapabilities.prototype = {
   },
 
   isWindowPrivate() {
     return PrivateBrowsingUtils.isContentWindowPrivate(this.window);
   },
 
   contractID: "@mozilla.org/aboutcapabilities;1",
   classID: Components.ID("{4c2b1f46-e637-4a91-8108-8a9fb7aab91d}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsAboutCapabilities]);
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -849,17 +849,17 @@ var PanelMultiView = class extends Assoc
 
     // Set the viewContainer dimensions to make sure only the current view is
     // visible.
     let olderView = reverse ? nextPanelView : prevPanelView;
     this._viewContainer.style.minHeight = olderView.knownHeight + "px";
     this._viewContainer.style.height = prevPanelView.knownHeight + "px";
     this._viewContainer.style.width = prevPanelView.knownWidth + "px";
     // Lock the dimensions of the window that hosts the popup panel.
-    let rect = this._panel.popupBoxObject.getOuterScreenRect();
+    let rect = this._panel.getOuterScreenRect();
     this._panel.setAttribute("width", rect.width);
     this._panel.setAttribute("height", rect.height);
 
     let viewRect;
     if (reverse) {
       // Use the cached size when going back to a previous view, but not when
       // reopening a subview, because its contents may have changed.
       viewRect = { width: nextPanelView.knownWidth,
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -70,18 +70,18 @@ const kDownloadsStringsRequiringPluralFo
   otherDownloads3: true
 };
 
 const kMaxHistoryResultsForLimitedView = 42;
 
 const kPrefBranch = Services.prefs.getBranch("browser.download.");
 
 var PrefObserver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
   getPref(name) {
     try {
       switch (typeof this.prefs[name]) {
         case "boolean":
           return kPrefBranch.getBoolPref(name);
       }
     } catch (ex) { }
     return this.prefs[name];
--- a/browser/components/enterprisepolicies/EnterprisePolicies.js
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.js
@@ -64,19 +64,19 @@ function EnterprisePoliciesManager() {
   Services.obs.addObserver(this, "final-ui-startup", true);
   Services.obs.addObserver(this, "sessionstore-windows-restored", true);
   Services.obs.addObserver(this, "EnterprisePolicies:Restart", true);
 }
 
 EnterprisePoliciesManager.prototype = {
   // for XPCOM
   classID:          Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIEnterprisePolicies]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference,
+                                          Ci.nsIEnterprisePolicies]),
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: EnterprisePoliciesFactory,
 
   _initialize() {
     let provider = this._chooseProvider();
 
     if (!provider) {
--- a/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
+++ b/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
@@ -46,17 +46,17 @@ function EnterprisePoliciesManagerConten
 
   Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
   Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
 }
 
 EnterprisePoliciesManagerContent.prototype = {
   // for XPCOM
   classID:          Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIEnterprisePolicies]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIEnterprisePolicies]),
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: EnterprisePoliciesFactory,
 
   _status: Ci.nsIEnterprisePolicies.INACTIVE,
 
   _disallowedFeatures: [],
 
--- a/browser/components/enterprisepolicies/helpers/WebsiteFilter.jsm
+++ b/browser/components/enterprisepolicies/helpers/WebsiteFilter.jsm
@@ -74,18 +74,18 @@ function WebsiteFilter(blocklist, except
   if (exceptionArray.length) {
     this._exceptionsPatterns = new MatchPatternSet(exceptionArray);
   }
 
   Services.obs.addObserver(this, "http-on-modify-request", true);
 }
 
 WebsiteFilter.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   observe(subject, topic, data) {
     let channel, isDocument = false;
     try {
       channel = subject.QueryInterface(Ci.nsIHttpChannel);
       isDocument = channel.isDocument;
     } catch (e) {
       return;
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_search_engine.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_search_engine.js
@@ -155,21 +155,21 @@ add_task(async function test_opensearch_
 
 add_task(async function test_AddSearchProvider() {
   // Mock the modal error dialog
   let mockPrompter = {
     promptCount: 0,
     alert() {
       this.promptCount++;
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
   };
   let windowWatcher = {
     getNewPrompter: () => mockPrompter,
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
   };
   let origWindowWatcher = Services.ww;
   Services.ww = windowWatcher;
   registerCleanupFunction(() => {
     Services.ww = origWindowWatcher;
   });
 
   let engineURL = getRootDirectory(gTestPath) + "opensearchEngine.xml";
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -1164,17 +1164,17 @@ this.tabs = class extends ExtensionAPI {
                       resolve(retval == 0 ? "saved" : "replaced");
                     }
                   },
                   onStatusChange: function(webProgress, request, status, message) {
                     if (status != 0) {
                       resolve(retval == 0 ? "not_saved" : "not_replaced");
                     }
                   },
-                  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener]),
+                  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener]),
                 };
 
                 activeTab.linkedBrowser.print(activeTab.linkedBrowser.outerWindowID, printSettings, printProgressListener);
               } else {
                 // Cancel clicked (retval == 1)
                 resolve("canceled");
               }
             });
--- a/browser/components/feeds/FeedConverter.js
+++ b/browser/components/feeds/FeedConverter.js
@@ -321,25 +321,20 @@ FeedConverter.prototype = {
   onStopRequest(request, context, status) {
     if (this._processor)
       this._processor.onStopRequest(request, context, status);
   },
 
   /**
    * See nsISupports.idl
    */
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIFeedResultListener) ||
-        iid.equals(Ci.nsIStreamConverter) ||
-        iid.equals(Ci.nsIStreamListener) ||
-        iid.equals(Ci.nsIRequestObserver) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFeedResultListener",
+                                          "nsIStreamConverter",
+                                          "nsIStreamListener",
+                                          "nsIRequestObserver"]),
 };
 
 /**
  * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
  * converter completes.
  */
 function FeedResultService() {
 }
@@ -451,23 +446,18 @@ FeedResultService.prototype = {
   },
 
   createInstance(outer, iid) {
     if (outer != null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return this.QueryInterface(iid);
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIFeedResultService) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIFeedResultService",
+                                          "nsIFactory"]),
 };
 
 
 var components = [FeedConverter,
                   FeedResultService];
 
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -976,13 +976,13 @@ FeedWriter.prototype = {
         }
       });
     } else {
       subscribeCallback();
     }
   },
 
   classID: FEEDWRITER_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsIDOMGlobalPropertyInitializer])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]);
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -43,38 +43,28 @@ WebContentConverter.prototype = {
 
   onStartRequest(request, context) {
     let wccr =
         Cc[WCCR_CONTRACTID].
         getService(Ci.nsIWebContentConverterService);
     wccr.loadPreferredHandler(request);
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIStreamConverter) ||
-        iid.equals(Ci.nsIStreamListener) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIStreamConverter",
+                                          "nsIStreamListener"]),
 };
 
 let WebContentConverterFactory = {
   createInstance(outer, iid) {
     if (outer != null)
       throw Cr.NS_ERROR_NO_AGGREGATION;
     return new WebContentConverter().QueryInterface(iid);
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
 };
 
 function ServiceInfo(contentType, uri, name) {
   this._contentType = contentType;
   this._uri = uri;
   this._name = name;
 }
 ServiceInfo.prototype = {
@@ -116,22 +106,17 @@ ServiceInfo.prototype = {
 
   /**
    * See nsIWebContentHandlerInfo
    */
   getHandlerURI(uri) {
     return this._uri.replace(/%s/gi, encodeURIComponent(uri));
   },
 
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIWebContentHandlerInfo) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  QueryInterface: ChromeUtils.generateQI(["nsIWebContentHandlerInfo"]),
 };
 
 const Utils = {
   makeURI(aURL, aOriginCharset, aBaseURI) {
     return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
   },
 
   checkAndGetURI(aURIString, aContentWindow) {
@@ -860,17 +845,17 @@ WebContentConverterRegistrar.prototype =
     return this.QueryInterface(iid);
   },
 
   classID: WCCR_CLASSID,
 
   /**
    * See nsISupports
    */
-  QueryInterface: XPCOMUtils.generateQI(
+  QueryInterface: ChromeUtils.generateQI(
      [Ci.nsIWebContentConverterService,
       Ci.nsIWebContentHandlerRegistrar,
       Ci.nsIObserver,
       Ci.nsIFactory]),
 
   _xpcom_categories: [{
     category: "app-startup",
     service: true
@@ -1039,17 +1024,17 @@ WebContentConverterRegistrarContent.prot
     return this.QueryInterface(iid);
   },
 
   classID: WCCR_CLASSID,
 
   /**
    * See nsISupports
    */
-  QueryInterface: XPCOMUtils.generateQI(
+  QueryInterface: ChromeUtils.generateQI(
                      [Ci.nsIWebContentHandlerRegistrar,
                       Ci.nsIWebContentConverterService,
                       Ci.nsIFactory])
 };
 
 this.NSGetFactory =
   (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) ?
     XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrarContent]) :
--- a/browser/components/migration/AutoMigrate.jsm
+++ b/browser/components/migration/AutoMigrate.jsm
@@ -674,17 +674,17 @@ const AutoMigrate = {
       return;
     }
 
     let url = Services.urlFormatter.formatURL(rawURL);
     url = url.replace("%IMPORTEDBROWSER%", encodeURIComponent(migrationBrowser));
     chromeWindow.openTrustedLinkIn(url, "tab");
   },
 
-  QueryInterface: XPCOMUtils.generateQI(
+  QueryInterface: ChromeUtils.generateQI(
     [Ci.nsIObserver, Ci.nsINavBookmarkObserver, Ci.nsISupportsWeakReference]
   ),
 
   /**
    * Undo action called by the UndoNotification or by the newtab
    * @param chromeWindow A reference to the window in which to open a link.
    */
   undoAutoMigration(chromeWindow) {
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -80,17 +80,17 @@ function getMigrationBundle() {
  * 3. Set classDescription, contractID and classID for your migrator, and set
  *    NSGetFactory appropriately.
  * 4. If the migrator supports multiple profiles, override the sourceProfiles
  *    Here we default for single-profile migrator.
  * 5. Implement getResources(aProfile) (see below).
  * 6. For startup-only migrators, override |startupOnlyMigrator|.
  */
 var MigratorPrototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIBrowserProfileMigrator]),
 
   /**
    * OVERRIDE IF AND ONLY IF the source supports multiple profiles.
    *
    * Returns array of profile objects from which data may be imported. The object
    * should have the following keys:
    *   id - a unique string identifier for the profile
    *   name - a pretty name to display to the user in the UI
--- a/browser/components/migration/ProfileMigrator.js
+++ b/browser/components/migration/ProfileMigrator.js
@@ -7,15 +7,15 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 
 function ProfileMigrator() {
 }
 
 ProfileMigrator.prototype = {
   migrate: MigrationUtils.startupMigration.bind(MigrationUtils),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIProfileMigrator]),
   classDescription: "Profile Migrator",
   contractID: "@mozilla.org/toolkit/profile-migrator;1",
   classID: Components.ID("6F8BB968-C14F-4D6F-9733-6C6737B35DCE"),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]);
--- a/browser/components/newtab/aboutNewTabService.js
+++ b/browser/components/newtab/aboutNewTabService.js
@@ -87,17 +87,17 @@ AboutNewTabService.prototype = {
   _activityStreamEnabled: false,
   _activityStreamPrerender: false,
   _activityStreamPath: "",
   _activityStreamDebug: false,
   _overridden: false,
   willNotifyUser: false,
 
   classID: Components.ID("{dfcd2adc-7867-4d3a-ba70-17501f208142}"),
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIAboutNewTabService,
     Ci.nsIObserver
   ]),
   _xpcom_categories: [{
     service: true
   }],
 
   observe(subject, topic, data) {
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -281,20 +281,20 @@ nsBrowserContentHandler.prototype = {
     createInstance: function bch_factory_ci(outer, iid) {
       if (outer)
         throw Cr.NS_ERROR_NO_AGGREGATION;
       return gBrowserContentHandler.QueryInterface(iid);
     }
   },
 
   /* nsISupports */
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler,
-                                         Ci.nsIBrowserHandler,
-                                         Ci.nsIContentHandler,
-                                         Ci.nsICommandLineValidator]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsICommandLineHandler,
+                                          Ci.nsIBrowserHandler,
+                                          Ci.nsIContentHandler,
+                                          Ci.nsICommandLineValidator]),
 
   /* nsICommandLineHandler */
   handle: function bch_handle(cmdLine) {
     if (cmdLine.handleFlag("browser", false)) {
       openBrowserWindow(cmdLine);
       cmdLine.preventDefault = true;
     }
 
@@ -692,23 +692,17 @@ function handURIToExistingBrowser(uri, l
 
 function nsDefaultCommandLineHandler() {
 }
 
 nsDefaultCommandLineHandler.prototype = {
   classID: Components.ID("{47cd0651-b1be-4a0f-b5c4-10e5a573ef71}"),
 
   /* nsISupports */
-  QueryInterface: function dch_QI(iid) {
-    if (!iid.equals(Ci.nsISupports) &&
-        !iid.equals(Ci.nsICommandLineHandler))
-      throw Cr.NS_ERROR_NO_INTERFACE;
-
-    return this;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsICommandLineHandler"]),
 
   _haveProfile: false,
 
   /* nsICommandLineHandler */
   handle: function dch_handle(cmdLine) {
     var urilist = [];
 
     if (AppConstants.platform == "win") {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2724,18 +2724,18 @@ BrowserGlue.prototype = {
     } else {
       AppMenuNotifications.removeNotification("fxa-needs-authentication");
     }
   },
 
   // for XPCOM
   classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(BrowserGlue),
 };
 
 /**
  * ContentPermissionIntegration is responsible for showing the user
  * simple permission prompts when content requests additional
  * capabilities.
@@ -2784,17 +2784,17 @@ const ContentPermissionIntegration = {
   },
 };
 
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
   classID:          Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionPrompt]),
 
   /**
    * This implementation of nsIContentPermissionPrompt.prompt ensures
    * that there's only one nsIContentPermissionType in the request,
    * and that it's of type nsIContentPermissionType. Failing to
    * satisfy either of these conditions will result in this method
    * throwing NS_ERRORs. If the combined ContentPermissionIntegration
    * cannot construct a prompt for this particular request, an
@@ -3037,17 +3037,17 @@ var DefaultBrowserCheck = {
  */
 var JawsScreenReaderVersionCheck = {
   _prompted: false,
 
   init() {
     Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
   observe(subject, topic, data) {
     if (topic == "a11y-init-or-shutdown" && data == "1") {
       Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
     }
   },
 
   onWindowsRestored() {
--- a/browser/components/payments/content/paymentDialogWrapper.js
+++ b/browser/components/payments/content/paymentDialogWrapper.js
@@ -35,17 +35,17 @@ XPCOMUtils.defineLazyGetter(this, "formA
 });
 
 var paymentDialogWrapper = {
   componentsLoaded: new Map(),
   frame: null,
   mm: null,
   request: null,
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
   ]),
 
   /**
    * Note: This method is async because formAutofillStorage plans to become async.
    *
    * @param {string} guid
--- a/browser/components/payments/paymentUIService.js
+++ b/browser/components/payments/paymentUIService.js
@@ -32,17 +32,17 @@ function PaymentUIService() {
       prefix: "Payment UI Service",
     });
   });
   this.log.debug("constructor");
 }
 
 PaymentUIService.prototype = {
   classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
   DIALOG_URL: "chrome://payments/content/paymentDialogWrapper.xhtml",
   REQUEST_ID_PREFIX: "paymentRequest-",
 
   // nsIPaymentUIService implementation:
 
   showPayment(requestId) {
     this.log.debug("showPayment:", requestId);
     let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -367,22 +367,17 @@ var BookmarkPropertiesPanel = {
           this._height += -oldHeight + newHeight;
           elementsHeight.set(id, newHeight);
         }
         break;
     }
   },
 
   // nsISupports
-  QueryInterface: function BPP_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_NOINTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI([]),
 
   _element: function BPP__element(aID) {
     return document.getElementById("editBMPanel_" + aID);
   },
 
   onDialogUnload() {
     // gEditItemOverlay does not exist anymore here, so don't rely on it.
     this._mutationObserver.disconnect();
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -14,16 +14,19 @@ function PlacesViewBase(aPlace, aOptions
   if ("viewElt" in aOptions)
     this._viewElt = aOptions.viewElt;
   this.options = aOptions;
   this._controller = new PlacesController(this);
   this.place = aPlace;
   this._viewElt.controllers.appendController(this._controller);
 }
 
+PlacesViewBase.interfaces = [Ci.nsINavHistoryResultObserver,
+                             Ci.nsISupportsWeakReference];
+
 PlacesViewBase.prototype = {
   // The xul element that holds the entire view.
   _viewElt: null,
   get viewElt() {
     return this._viewElt;
   },
 
   get associatedElement() {
@@ -36,19 +39,18 @@ PlacesViewBase.prototype = {
 
   // The xul element that represents the root container.
   _rootElt: null,
 
   // Set to true for views that are represented by native widgets (i.e.
   // the native mac menu).
   _nativeView: false,
 
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsINavHistoryResultObserver,
-     Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI(
+    PlacesViewBase.interfaces),
 
   _place: "",
   get place() {
     return this._place;
   },
   set place(val) {
     this._place = val;
 
@@ -1003,22 +1005,18 @@ function PlacesToolbar(aPlace) {
 }
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
               "mousemove", "mouseover", "mouseout"],
 
-  QueryInterface: function PT_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsITimerCallback))
-      return this;
-
-    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsITimerCallback",
+                                          ...PlacesViewBase.interfaces]),
 
   uninit: function PT_uninit() {
     this._removeEventListeners(this._viewElt, this._cbEvents, false);
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
     this._removeEventListeners(window, ["resize", "unload"], false);
     this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
@@ -1970,20 +1968,16 @@ function PlacesPanelMenuView(aPlace, aVi
   this.options = aOptions;
 
   PlacesViewBase.call(this, aPlace, aOptions);
 }
 
 PlacesPanelMenuView.prototype = {
   __proto__: PlacesViewBase.prototype,
 
-  QueryInterface: function PAMV_QueryInterface(aIID) {
-    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
-  },
-
   uninit: function PAMV_uninit() {
     PlacesViewBase.prototype.uninit.apply(this, arguments);
   },
 
   _insertNewItem:
   function PAMV__insertNewItem(aChild, aInsertionNode, aBefore = null) {
     this._domNodes.delete(aChild);
 
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -83,17 +83,17 @@ PlacesController.prototype = {
    */
   _view: null,
 
   // This is used in certain views to disable user actions on the places tree
   // views. This avoids accidental deletion/modification when the user is not
   // actually organising the trees.
   disableUserActions: false,
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIClipboardOwner
   ]),
 
   // nsIClipboardOwner
   LosingOwnership: function PC_LosingOwnership(aXferable) {
     this.cutNodes = [];
   },
 
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -460,17 +460,17 @@ var gEditItemOverlay = {
                                       this._folderMenuList.selectedIndex);
 
     // Hide the folders-separator if no folder is annotated as recently-used
     this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
     this._folderMenuList.disabled = this.readOnly;
   },
 
   QueryInterface:
-  XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]),
+  ChromeUtils.generateQI([Ci.nsINavBookmarkObserver]),
 
   _element(aID) {
     return document.getElementById("editBMPanel_" + aID);
   },
 
   uninitPanel(aHideCollapsibleElements) {
     if (aHideCollapsibleElements) {
       // Hide the folder tree if it was previously visible.
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -211,17 +211,17 @@
         },
 
         notify: function OF__notify(aTimer) {
           // Function to process all timer notifications.
 
           if (aTimer == this._folder.openTimer) {
             // Timer to open a submenu that's being dragged over.
             this._folder.elt.lastChild.setAttribute("autoopened", "true");
-            this._folder.elt.lastChild.showPopup(this._folder.elt);
+            this._folder.elt.lastChild.openPopup();
             this._folder.openTimer = null;
           } else if (aTimer == this._folder.closeTimer) {
             // Timer to close a submenu that's been dragged off of.
             // Only close the submenu if the mouse isn't being dragged over any
             // of its child menus.
             var draggingOverChild = PlacesControllerDragHelper
                                     .draggingOverChildNode(this._folder.elt);
             if (draggingOverChild)
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -191,22 +191,17 @@ var PlacesOrganizer = {
 
     if (!Services.policies.isAllowed("profileImport")) {
       document.getElementById("OrganizerCommand_browserImport").setAttribute("disabled", true);
     }
 
     ContentArea.focus();
   },
 
-  QueryInterface: function PO_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_NOINTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI([]),
 
   handleEvent: function PO_handleEvent(aEvent) {
     if (aEvent.type != "AppCommand")
       return;
 
     aEvent.stopPropagation();
     switch (aEvent.command) {
       case "Back":
--- a/browser/components/places/tests/browser/browser_editBookmark_keywords.js
+++ b/browser/components/places/tests/browser/browser_editBookmark_keywords.js
@@ -11,17 +11,17 @@ add_task(async function() {
         onItemAdded() {},
         onItemRemoved() {},
         onItemVisited() {},
         onItemMoved() {},
         onItemChanged(id, property, isAnno, value) {
           PlacesUtils.bookmarks.removeObserver(this);
           resolve({ property, value });
         },
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+        QueryInterface: ChromeUtils.generateQI([Ci.nsINavBookmarkObserver])
       });
     });
   }
 
   let tab = await BrowserTestUtils.openNewForegroundTab({
     gBrowser,
     opening: TEST_URL,
     waitForStateStop: true
--- a/browser/components/places/tests/browser/browser_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_views_liveupdate.js
@@ -122,17 +122,17 @@ add_task(async function test() {
   }
 });
 
 /**
  * The observer is where magic happens, for every change we do it will look for
  * nodes positions in the affected views.
  */
 var bookmarksObserver = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsINavBookmarkObserver,
     Ci.nsIAnnotationObserver
   ]),
 
   // nsIAnnotationObserver
   onItemAnnotationSet() {},
   onItemAnnotationRemoved() {},
   onPageAnnotationSet() {},
@@ -270,17 +270,17 @@ function getNodeForToolbarItem(aItemId, 
         let valid = aValidator ? aValidator(child) : true;
         return [child._placesNode, i - staticNodes, valid];
       }
 
       // Don't search in queries, they could contain our item in a
       // different position.  Search only folders
       if (PlacesUtils.nodeIsFolder(child._placesNode)) {
         var popup = child.lastChild;
-        popup.showPopup(popup);
+        popup.openPopup();
         var foundNode = findNode(popup);
         popup.hidePopup();
         if (foundNode[0] != null)
           return foundNode;
       }
     }
     return [null, null];
   }
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -71,17 +71,17 @@ function rebuildSmartBookmarks() {
   let consoleListener = {
     observe(aMsg) {
       if (aMsg.message.startsWith("[JavaScript Warning:")) {
         // TODO (Bug 1300416): Ignore spurious strict warnings.
         return;
       }
       do_throw("Got console message: " + aMsg.message);
     },
-    QueryInterface: XPCOMUtils.generateQI([ Ci.nsIConsoleListener ]),
+    QueryInterface: ChromeUtils.generateQI([ Ci.nsIConsoleListener ]),
   };
   Services.console.reset();
   Services.console.registerListener(consoleListener);
   registerCleanupFunction(() => {
     try {
       Services.console.unregisterListener(consoleListener);
     } catch (ex) { /* will likely fail */ }
   });
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1314,17 +1314,17 @@ var gMainPane = {
     Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_READER, this);
 
     Services.prefs.removeObserver(PREF_CONTAINERS_EXTENSION, this);
   },
 
 
   // nsISupports
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   // nsIObserver
 
   async observe(aSubject, aTopic, aData) {
     if (aTopic == "nsPref:changed") {
       if (aData == PREF_CONTAINERS_EXTENSION) {
         this.readBrowserContainersCheckbox();
         return;
@@ -3017,24 +3017,17 @@ FeedHandlerInfo.prototype = {
       return this._possibleApplicationHandlers;
 
     // A minimal implementation of nsIMutableArray.  It only supports the two
     // methods its callers invoke, namely appendElement and nsIArray::enumerate.
     this._possibleApplicationHandlers = {
       _inner: [],
       _removed: [],
 
-      QueryInterface(aIID) {
-        if (aIID.equals(Ci.nsIMutableArray) ||
-          aIID.equals(Ci.nsIArray) ||
-          aIID.equals(Ci.nsISupports))
-          return this;
-
-        throw Cr.NS_ERROR_NO_INTERFACE;
-      },
+      QueryInterface: ChromeUtils.generateQI(["nsIMutableArray", "nsIArray"]),
 
       get length() {
         return this._inner.length;
       },
 
       enumerate() {
         return new ArrayEnumerator(this._inner);
       },
--- a/browser/components/preferences/in-content/tests/browser_advanced_update.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_update.js
@@ -13,17 +13,17 @@ const mockUpdateManager = {
   contractId: "@mozilla.org/updates/update-manager;1",
 
   _mockClassId: uuidGenerator.generateUUID(),
 
   _originalClassId: "",
 
   _originalFactory: null,
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateManager]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIUpdateManager]),
 
   createInstance(outer, iiD) {
     if (outer) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(iiD);
   },
 
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -65,17 +65,17 @@ Tree.prototype = {
     return "";
   },
   getColumnProperties(column) {
     return "";
   },
   getCellProperties(row, column) {
     return "";
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITreeView])
 };
 
 function Lang(aCode, label) {
   this.langCode = aCode;
   this._label = label;
 }
 
 Lang.prototype = {
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -83,17 +83,17 @@ add_task(async function test_toggleTrack
 
   let { win, tab } = await openAboutPrivateBrowsing();
 
   // Set up the observer for the preference change before triggering the action.
   let prefBranch =
       Services.prefs.getBranch("privacy.trackingprotection.pbmode.");
   let waitForPrefChanged = () => new Promise(resolve => {
     let prefObserver = {
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
       observe() {
         prefBranch.removeObserver("enabled", prefObserver);
         resolve();
       },
     };
     prefBranch.addObserver("enabled", prefObserver);
   });
 
--- a/browser/components/search/test/head.js
+++ b/browser/components/search/test/head.js
@@ -88,18 +88,18 @@ let promiseStateChangeFrameScript = "dat
   () => {
     ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
     /* globals docShell, sendAsyncMessage */
 
     const global = this;
     const LISTENER = Symbol("listener");
     let listener = {
-      QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference",
-                                             "nsIWebProgressListener"]),
+      QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference",
+                                              "nsIWebProgressListener"]),
 
       onStateChange: function onStateChange(webProgress, req, flags, status) {
         // Only care about top-level document starts
         if (!webProgress.isTopLevel ||
             !(flags & Ci.nsIWebProgressListener.STATE_START)) {
           return;
         }
 
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -366,17 +366,17 @@ ContentRestoreInternal.prototype = {
 function HistoryListener(docShell, callback) {
   let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
   webNavigation.sessionHistory.legacySHistory.addSHistoryListener(this);
 
   this.webNavigation = webNavigation;
   this.callback = callback;
 }
 HistoryListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsISHistoryListener,
     Ci.nsISupportsWeakReference
   ]),
 
   uninstall() {
     let shistory = this.webNavigation.sessionHistory.legacySHistory;
     if (shistory) {
       shistory.removeSHistoryListener(this);
@@ -446,17 +446,17 @@ function ProgressListener(docShell, call
                             .getInterface(Ci.nsIWebProgress);
   webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
 
   this.webProgress = webProgress;
   this.callbacks = callbacks;
 }
 
 ProgressListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference
   ]),
 
   uninstall() {
     this.webProgress.removeProgressListener(this);
   },
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -433,17 +433,17 @@ var SessionStore = {
     }
   },
 };
 
 // Freeze the SessionStore object. We don't want anyone to modify it.
 Object.freeze(SessionStore);
 
 var SessionStoreInternal = {
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ]),
 
   _globalState: new GlobalState(),
 
   // A counter to be used to generate a unique ID for each closed tab or window.
   _nextClosedId: 0,
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -137,18 +137,18 @@ var StateChangeNotifier = {
 
     if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
       this.notifyObservers("onPageLoadStarted");
     } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
       this.notifyObservers("onPageLoadCompleted");
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                          Ci.nsISupportsWeakReference])
 };
 
 /**
  * Listens for and handles content events that we need for the
  * session store service to be notified of state changes in content.
  */
 var EventListener = {
 
@@ -442,17 +442,17 @@ var SessionHistoryListener = {
   OnLengthChanged(aCount) {
     // Ignore, the method is implemented so that XPConnect doesn't throw!
   },
 
   OnIndexChanged(aIndex) {
     // Ignore, the method is implemented so that XPConnect doesn't throw!
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsISHistoryListener,
     Ci.nsISupportsWeakReference
   ])
 };
 
 /**
  * Listens for scroll position changes. Whenever the user scrolls the top-most
  * frame we update the scroll position and will restore it when requested.
@@ -694,18 +694,18 @@ var PrivacyListener = {
     }
   },
 
   // Ci.nsIPrivacyTransitionObserver
   privateModeChanged(enabled) {
     MessageQueue.push("isPrivate", () => enabled || null);
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivacyTransitionObserver,
-                                         Ci.nsISupportsWeakReference])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPrivacyTransitionObserver,
+                                          Ci.nsISupportsWeakReference])
 };
 
 /**
  * A message queue that takes collected data and will take care of sending it
  * to the chrome process. It allows flushing using synchronous messages and
  * takes care of any race conditions that might occur because of that. Changes
  * will be batched if they're pushed in quick succession to avoid a message
  * flood.
--- a/browser/components/sessionstore/nsSessionStartup.js
+++ b/browser/components/sessionstore/nsSessionStartup.js
@@ -351,15 +351,15 @@ SessionStartup.prototype = {
   /**
    * Get whether the previous session crashed.
    */
   get previousSessionCrashed() {
     return this._previousSessionCrashed;
   },
 
   /* ........ QueryInterface .............. */
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISessionStartup]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference,
+                                          Ci.nsISessionStartup]),
   classID: Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}")
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
--- a/browser/components/sessionstore/test/browser_599909.js
+++ b/browser/components/sessionstore/test/browser_599909.js
@@ -106,17 +106,17 @@ function checkAutocompleteResults(aExpec
 
       executeSoon(aCallback);
     },
     setSelectedIndex() {},
     get searchCount() { return this.searches.length; },
     getSearchAt(aIndex) {
       return this.searches[aIndex];
     },
-    QueryInterface: XPCOMUtils.generateQI([
+    QueryInterface: ChromeUtils.generateQI([
       Ci.nsIAutoCompleteInput,
       Ci.nsIAutoCompletePopup,
     ])
   };
 
   info("Searching open pages.");
   gController.startSearch(RESTRICT_TOKEN_OPENPAGE);
 }
--- a/browser/components/sessionstore/test/browser_async_remove_tab.js
+++ b/browser/components/sessionstore/test/browser_async_remove_tab.js
@@ -43,17 +43,17 @@ function promiseNewLocationAndHistoryEnt
 
     return new Promise(resolve => {
       let listener = {
         OnHistoryReplaceEntry() {
           shistory.removeSHistoryListener(this);
           resolve();
         },
 
-        QueryInterface: XPCOMUtils.generateQI([
+        QueryInterface: ChromeUtils.generateQI([
           Ci.nsISHistoryListener,
           Ci.nsISupportsWeakReference
         ])
       };
 
       shistory.addSHistoryListener(listener);
 
       /* Keep the weak shistory listener alive. */
@@ -73,17 +73,17 @@ function promiseHistoryEntryReplacedNonR
     let shistory = browser.webNavigation.sessionHistory.legacySHistory;
 
     let listener = {
       OnHistoryReplaceEntry() {
         shistory.removeSHistoryListener(this);
         executeSoon(resolve);
       },
 
-      QueryInterface: XPCOMUtils.generateQI([
+      QueryInterface: ChromeUtils.generateQI([
         Ci.nsISHistoryListener,
         Ci.nsISupportsWeakReference
       ])
     };
 
     shistory.addSHistoryListener(listener);
     listeners.set(browser, listener);
   });
--- a/browser/components/sessionstore/test/browser_not_collect_when_idle.js
+++ b/browser/components/sessionstore/test/browser_not_collect_when_idle.js
@@ -20,17 +20,17 @@ var idleService = {
 
   _fireObservers(state) {
     for (let observer of this._observers.values()) {
       observer.observe(observer, state, null);
       this._activity.observerFires.push(state);
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIdleService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIIdleService]),
   idleTime: 19999,
 
   addIdleObserver(observer, time) {
     this._observers.add(observer);
     this._activity.addCalls.push(time);
   },
 
   removeIdleObserver(observer, time) {
--- a/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
+++ b/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
@@ -7,17 +7,17 @@ const SELFCHROMEURL =
 const Cm = Components.manager;
 
 const TESTCLASSID = "78742c04-3630-448c-9be3-6c5070f062de";
 
 const TESTURL = "about:testpageforsessionrestore#foo";
 
 
 let TestAboutPage = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
   getURIFlags(aURI) {
     // No CAN_ or MUST_LOAD_IN_CHILD means this loads in the parent:
     return Ci.nsIAboutModule.ALLOW_SCRIPT |
            Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
            Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
   },
 
   newChannel(aURI, aLoadInfo) {
--- a/browser/components/sessionstore/test/content.js
+++ b/browser/components/sessionstore/test/content.js
@@ -41,17 +41,17 @@ var historyListener = {
     sendAsyncMessage("ss-test:OnHistoryReload");
     return true;
   },
 
   OnHistoryReplaceEntry() {
     sendAsyncMessage("ss-test:OnHistoryReplaceEntry");
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsISHistoryListener,
     Ci.nsISupportsWeakReference
   ])
 };
 
 var {sessionHistory} = docShell.QueryInterface(Ci.nsIWebNavigation);
 if (sessionHistory) {
   sessionHistory.legacySHistory.addSHistoryListener(historyListener);
--- a/browser/components/shell/HeadlessShell.jsm
+++ b/browser/components/shell/HeadlessShell.jsm
@@ -34,18 +34,18 @@ function loadContentWindow(webNavigation
         let contentWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                     .getInterface(Ci.nsIDOMWindow);
         progressListeners.delete(progressListener);
         webProgress.removeProgressListener(progressListener);
         contentWindow.addEventListener("load", (event) => {
           resolve(contentWindow);
         }, { once: true });
       },
-      QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
-                                             "nsISupportsWeakReference"])
+      QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
+                                              "nsISupportsWeakReference"])
     };
     progressListeners.set(progressListener, progressListener);
     webProgress.addProgressListener(progressListener,
                                     Ci.nsIWebProgress.NOTIFY_LOCATION);
   });
 }
 
 async function takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight, path, url) {
--- a/browser/components/tests/browser/browser_contentpermissionprompt.js
+++ b/browser/components/tests/browser/browser_contentpermissionprompt.js
@@ -21,17 +21,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
  *        Example: "geo", "desktop-notification".
  * @return nsIContentPermissionType implementation.
  */
 function MockContentPermissionType(type) {
   this.type = type;
 }
 
 MockContentPermissionType.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
   // We expose the wrappedJSObject so that we can be sure
   // in some of our tests that we're passing the right
   // nsIContentPermissionType around.
   wrappedJSObject: this,
 };
 
 /**
  * This is a partial implementation of nsIContentPermissionRequest.
@@ -44,17 +44,17 @@ MockContentPermissionType.prototype = {
 function MockContentPermissionRequest(typesArray) {
   this.types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
   for (let type of typesArray) {
     this.types.appendElement(type);
   }
 }
 
 MockContentPermissionRequest.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
   // We expose the wrappedJSObject so that we can be sure
   // in some of our tests that we're passing the right
   // nsIContentPermissionRequest around.
   wrappedJSObject: this,
   // For some of our tests, we want to make sure that the
   // request is cancelled, so we add some instrumentation here
   // to check that cancel() is called.
   cancel() {
@@ -95,17 +95,17 @@ add_task(async function test_multiple_ty
 
 /**
  * Tests that if the nsIContentPermissionRequest has a type that
  * does not implement nsIContentPermissionType that NS_NOINTERFACE
  * is thrown, and the request is cancelled.
  */
 add_task(async function test_not_permission_type() {
   let mockRequest = new MockContentPermissionRequest([
-    { QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]) },
+    { QueryInterface: ChromeUtils.generateQI([]) },
   ]);
 
   Assert.throws(() => { ContentPermissionPrompt.prompt(mockRequest); },
                 /NS_NOINTERFACE/);
   Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
 });
 
 /**
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -52,17 +52,17 @@ function startupRecorder() {
     code: {},
     prefStats: {},
   };
   this.done = new Promise(resolve => { this._resolve = resolve; });
 }
 startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   record(name) {
     if (!Services.prefs.getBoolPref("browser.startup.record", false))
       return;
 
     this.data.code[name] = {
       components: this.loader.loadedComponents(),
       modules: this.loader.loadedModules(),
--- a/browser/components/translation/TranslationContentHandler.jsm
+++ b/browser/components/translation/TranslationContentHandler.jsm
@@ -100,18 +100,18 @@ TranslationContentHandler.prototype = {
         state: STATE_OFFER,
         originalShown: true,
         detectedLanguage: result.language
       };
       this.global.sendAsyncMessage("Translation:DocumentState", data);
     });
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                          Ci.nsISupportsWeakReference]),
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "Translation:TranslateDocument":
       {
         ChromeUtils.import("resource:///modules/translation/TranslationDocument.jsm");
 
         // If a TranslationDocument already exists for this document, it should
--- a/browser/components/uitour/test/browser_UITour.js
+++ b/browser/components/uitour/test/browser_UITour.js
@@ -238,17 +238,17 @@ var tests = [
   function test_info_1(done) {
     let popup = document.getElementById("UITourTooltip");
     let title = document.getElementById("UITourTooltipTitle");
     let desc = document.getElementById("UITourTooltipDescription");
     let icon = document.getElementById("UITourTooltipIcon");
     let buttons = document.getElementById("UITourTooltipButtons");
 
     popup.addEventListener("popupshown", function() {
-      is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
+      is(popup.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
       is(title.textContent, "test title", "Popup should have correct title");
       is(desc.textContent, "test text", "Popup should have correct description text");
       is(icon.src, "", "Popup should have no icon");
       is(buttons.hasChildNodes(), false, "Popup should have no buttons");
 
       popup.addEventListener("popuphidden", function() {
         popup.addEventListener("popupshown", function() {
           done();
@@ -266,30 +266,30 @@ var tests = [
     let popup = document.getElementById("UITourTooltip");
     let title = document.getElementById("UITourTooltipTitle");
     let desc = document.getElementById("UITourTooltipDescription");
     let icon = document.getElementById("UITourTooltipIcon");
     let buttons = document.getElementById("UITourTooltipButtons");
 
     await showInfoPromise("urlbar", "urlbar title", "urlbar text");
 
-    is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
+    is(popup.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
     is(title.textContent, "urlbar title", "Popup should have correct title");
     is(desc.textContent, "urlbar text", "Popup should have correct description text");
     is(icon.src, "", "Popup should have no icon");
     is(buttons.hasChildNodes(), false, "Popup should have no buttons");
 
     // Place the search bar in the navigation toolbar temporarily.
     await SpecialPowers.pushPrefEnv({ set: [
       ["browser.search.widget.inNavBar", true],
     ]});
 
     await showInfoPromise("search", "search title", "search text");
 
-    is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
+    is(popup.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
     is(title.textContent, "search title", "Popup should have correct title");
     is(desc.textContent, "search text", "Popup should have correct description text");
 
     await SpecialPowers.popPrefEnv();
   }),
   function test_getConfigurationVersion(done) {
     function callback(result) {
       ok(typeof result.version !== "undefined", "Check version isn't undefined.");
--- a/browser/components/uitour/test/browser_UITour_defaultBrowser.js
+++ b/browser/components/uitour/test/browser_UITour_defaultBrowser.js
@@ -5,17 +5,17 @@ var gContentAPI;
 var gContentWindow;
 var setDefaultBrowserCalled = false;
 
 Services.scriptloader
   .loadSubScript("chrome://mochikit/content/tests/SimpleTest/MockObjects.js", this);
 
 function MockShellService() {}
 MockShellService.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIShellService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIShellService]),
   isDefaultBrowser(aStartupCheck, aForAllTypes) { return false; },
   setDefaultBrowser(aClaimAllTypes, aForAllUsers) {
     setDefaultBrowserCalled = true;
   },
   shouldCheckDefaultBrowser: false,
   canSetDesktopBackground: false,
   BACKGROUND_TILE: 1,
   BACKGROUND_STRETCH: 2,
--- a/browser/components/uitour/test/browser_fxa.js
+++ b/browser/components/uitour/test/browser_fxa.js
@@ -31,17 +31,17 @@ var tests = [
   }),
 
   taskify(async function test_highlight_accountStatus_loggedIn() {
     gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, lastSync: new Date(), email: "foo@example.com" });
     await showMenuPromise("appMenu");
     await showHighlightPromise("accountStatus");
     let highlight = document.getElementById("UITourHighlightContainer");
     let expectedTarget = "appMenu-fxa-avatar";
-    is(highlight.popupBoxObject.anchorNode.id, expectedTarget, "Anchored on avatar");
+    is(highlight.anchorNode.id, expectedTarget, "Anchored on avatar");
     is(highlight.getAttribute("targetName"), "accountStatus", "Correct highlight target");
   }),
 ];
 
 function signOut() {
   // we always want a "localOnly" signout here...
   return fxAccounts.signOut(true);
 }
--- a/browser/components/uitour/test/browser_trackingProtection_tour.js
+++ b/browser/components/uitour/test/browser_trackingProtection_tour.js
@@ -67,17 +67,17 @@ async function checkToggleTarget(targetI
     is(available, expectedAvailable, "Target has expected availability.");
   };
   await testTargetAvailability(false);
   await showMenuPromise("controlCenter");
   await testTargetAvailability(true);
 
   await showInfoPromise(targetID, "This is " + targetID,
                         "My arrow should be on the side");
-  is(popup.popupBoxObject.alignmentPosition, "end_before",
+  is(popup.alignmentPosition, "end_before",
      "Check " + targetID + " position");
 
   let hideMenuPromise =
         promisePanelElementHidden(window, gIdentityHandler._identityPopup);
   await gContentAPI.hideMenu("controlCenter");
   await hideMenuPromise;
 
   ok(!is_visible(popup), "The tooltip should now be hidden.");
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -113,17 +113,17 @@ function elementVisiblePromise(element, 
   return waitForConditionPromise(() => is_visible(element), "Timeout waiting for visibility: " + msg);
 }
 
 function elementHiddenPromise(element, msg) {
   return waitForConditionPromise(() => is_hidden(element), "Timeout waiting for invisibility: " + msg);
 }
 
 function waitForPopupAtAnchor(popup, anchorNode, nextTestFn, msg) {
-  waitForCondition(() => is_visible(popup) && popup.popupBoxObject.anchorNode == anchorNode,
+  waitForCondition(() => is_visible(popup) && popup.anchorNode == anchorNode,
                    () => {
                      ok(true, msg);
                      is_element_visible(popup, "Popup should be visible");
                      nextTestFn();
                    },
                    "Timeout waiting for popup at anchor: " + msg);
 }
 
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -17,17 +17,17 @@ const LINK_BLOCKED_EVENT = "newtab-linkB
 const PLACES_LINKS_CHANGED_DELAY_TIME = 1000; // time in ms to delay timer for places links changed events
 
 /**
  * Observer - a wrapper around history/bookmark observers to add the QueryInterface.
  */
 class Observer {
   constructor(dispatch, observerInterface) {
     this.dispatch = dispatch;
-    this.QueryInterface = XPCOMUtils.generateQI([observerInterface, Ci.nsISupportsWeakReference]);
+    this.QueryInterface = ChromeUtils.generateQI([observerInterface, Ci.nsISupportsWeakReference]);
   }
 }
 
 /**
  * HistoryObserver - observes events from PlacesUtils.history
  */
 class HistoryObserver extends Observer {
   constructor(dispatch) {
--- a/browser/extensions/followonsearch/content/followonsearch-fs.js
+++ b/browser/extensions/followonsearch/content/followonsearch-fs.js
@@ -136,17 +136,17 @@ gLastSearchQueue.push = function(...args
 // that started from Firefox
 let searchingGoogle = false;
 
 /**
  * Since most codes are in the URL, we can handle them via
  * a progress listener.
  */
 var webProgressListener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags)
   {
     if (aWebProgress.DOMWindow && (aWebProgress.DOMWindow != content)) {
       return;
     }
     try {
       if (!aWebProgress.isTopLevel ||
           // Not a URL
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -76,17 +76,17 @@ AutocompleteFactory.prototype = {
  */
 function AutofillProfileAutoCompleteSearch() {
   FormAutofillUtils.defineLazyLogGetter(this, "AutofillProfileAutoCompleteSearch");
 }
 AutofillProfileAutoCompleteSearch.prototype = {
   classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
   contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
   classDescription: "AutofillProfileAutoCompleteSearch",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompleteSearch]),
 
   // Begin nsIAutoCompleteSearch implementation
 
   /**
    * Searches for a given string and notifies a listener (either synchronously
    * or asynchronously) of the result
    *
    * @param {string} searchString the string to search for
@@ -204,17 +204,17 @@ AutofillProfileAutoCompleteSearch.protot
       });
 
       Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", data);
     });
   },
 };
 
 let ProfileAutocomplete = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   lastProfileAutoCompleteResult: null,
   lastProfileAutoCompleteFocusedInput: null,
   _registered: false,
   _factory: null,
 
   ensureRegistered() {
     if (this._registered) {
@@ -319,17 +319,17 @@ let ProfileAutocomplete = {
 };
 
 /**
  * Handles content's interactions for the process.
  *
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver]),
   /**
    * @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
    */
   _formsDetails: new WeakMap(),
 
   /**
    * @type {Set} Set of the fields with usable values in any saved profile.
    */
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -64,17 +64,17 @@ function FormAutofillParent() {
       this._updateSavedFieldNames();
     });
 
     return formAutofillStorage;
   });
 }
 
 FormAutofillParent.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   /**
    * Cache of the Form Autofill status (considering preferences and storage).
    */
   _active: null,
 
   /**
    * The status of Form Autofill's initialization.
--- a/browser/extensions/formautofill/MasterPassword.jsm
+++ b/browser/extensions/formautofill/MasterPassword.jsm
@@ -172,17 +172,17 @@ var MasterPassword = {
     if (!this.isUIBusy) {
       log.debug("waitForExistingDialog: Dialog isn't showing. isLoggedIn:", this.isLoggedIn);
       return this.isLoggedIn;
     }
 
     return new Promise((resolve) => {
       log.debug("waitForExistingDialog: Observing the open dialog");
       let observer = {
-        QueryInterface: XPCOMUtils.generateQI([
+        QueryInterface: ChromeUtils.generateQI([
           Ci.nsIObserver,
           Ci.nsISupportsWeakReference,
         ]),
 
         observe(subject, topic, data) {
           log.debug("waitForExistingDialog: Got notification:", topic);
           // Only run observer once.
           Services.obs.removeObserver(this, "passwordmgr-crypto-login");
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -21,17 +21,17 @@ class ProfileAutoCompleteResult {
   constructor(searchString, focusedFieldName, allFieldNames, matchingProfiles, {
     resultCode = null,
     isSecure = true,
     isInputAutofilled = false,
   }) {
     log.debug("Constructing new ProfileAutoCompleteResult:", [...arguments]);
 
     // nsISupports
-    this.QueryInterface = XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult]);
+    this.QueryInterface = ChromeUtils.generateQI([Ci.nsIAutoCompleteResult]);
 
     // The user's query string
     this.searchString = searchString;
     // The field name of the focused input.
     this._focusedFieldName = focusedFieldName;
     // The matching profiles contains the information for filling forms.
     this._matchingProfiles = matchingProfiles;
     // The default item that should be entered if none is selected
--- a/browser/extensions/formautofill/test/unit/test_masterPassword.js
+++ b/browser/extensions/formautofill/test/unit/test_masterPassword.js
@@ -41,24 +41,24 @@ let gMockPrompter = {
           "Please enter your master password.",
           "password prompt text should be as expected");
     equal(checkMsg, null, "checkMsg should be null");
     ok(this.passwordToTry, "passwordToTry should be non-null");
     password.value = this.passwordToTry;
     return true;
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
 };
 
 // Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
 // to call promptPassword. We return the mock one, above.
 let gWindowWatcher = {
   getNewPrompter: () => gMockPrompter,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
 };
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 
 let windowWatcherCID =
   MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
                          gWindowWatcher);
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -122,17 +122,17 @@ Factory.prototype = {
     if (this._classID2) {
       registrar.unregisterFactory(this._classID2, this._factory);
     }
     this._factory = null;
   },
 };
 
 var PdfJs = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
   _registered: false,
   _initialized: false,
 
   init: function init(remote) {
     if (Services.appinfo.processType !==
         Services.appinfo.PROCESS_TYPE_DEFAULT) {
       throw new Error("PdfJs.init should only get called " +
                       "in the parent process.");
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -804,22 +804,17 @@ PdfStreamConverter.prototype = {
   // properties required for XPCOM registration:
   classID: Components.ID("{d0c5195d-e798-49d4-b1d3-9324328b2291}"),
   classDescription: "pdf.js Component",
   contractID: "@mozilla.org/streamconv;1?from=application/pdf&to=*/*",
 
   classID2: Components.ID("{d0c5195d-e798-49d4-b1d3-9324328b2292}"),
   contractID2: "@mozilla.org/streamconv;1?from=application/pdf&to=text/html",
 
-  QueryInterface: XPCOMUtils.generateQI([
-      Ci.nsISupports,
-      Ci.nsIStreamConverter,
-      Ci.nsIStreamListener,
-      Ci.nsIRequestObserver
-  ]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamConverter, Ci.nsIStreamListener, Ci.nsIRequestObserver]),
 
   /*
    * This component works as such:
    * 1. asyncConvertData stores the listener
    * 2. onStartRequest creates a new channel, streams the viewer
    * 3. If range requests are supported:
    *      3.1. Leave the request open until the viewer is ready to switch to
    *           range requests.
--- a/browser/extensions/pocket/content/AboutPocket.jsm
+++ b/browser/extensions/pocket/content/AboutPocket.jsm
@@ -26,17 +26,17 @@ function AboutPage(chromeURL, aboutHost,
   this.chromeURL = chromeURL;
   this.aboutHost = aboutHost;
   this.classID = Components.ID(classID);
   this.description = description;
   this.uriFlags = uriFlags;
 }
 
 AboutPage.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
   getURIFlags(aURI) { // eslint-disable-line no-unused-vars
     return this.uriFlags;
   },
 
   newChannel(aURI, aLoadInfo) {
     let newURI = Services.io.newURI(this.chromeURL);
     let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
                                                             aLoadInfo);
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
+++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
@@ -103,17 +103,17 @@ let WebCompatReporter = {
       `${WebCompatReporter.endpoint}?${params}`,
       {inBackground: false, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
 
     // If we successfully got a screenshot blob, add a listener to know when
     // the new tab is loaded before sending it over.
     if (tabData && tabData.blob) {
       let browser = gBrowser.getBrowserForTab(tab);
       let loadedListener = {
-        QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
+        QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
           "nsISupportsWeakReference"]),
         onStateChange(webProgress, request, flags, status) {
           let isStopped = flags & Ci.nsIWebProgressListener.STATE_STOP;
           let isNetwork = flags & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
           if (isStopped && isNetwork && webProgress.isTopLevel) {
             let location;
             try {
               location = request.QueryInterface(Ci.nsIChannel).URI;
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -222,18 +222,18 @@ let URICountListener = {
 
   /**
    * Reset the counts. This should be called when breaking a session in Telemetry.
    */
   reset() {
     this._domainSet.clear();
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                          Ci.nsISupportsWeakReference]),
 };
 
 let urlbarListener = {
 
   // This is needed for recordUrlbarSelectedResultMethod().
   selectedIndex: -1,
 
   init() {
@@ -307,18 +307,18 @@ let urlbarListener = {
               .getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE")
               .add(actionType, idx);
     } else {
       Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " +
                      actionType);
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 };
 
 let BrowserUsageTelemetry = {
   _inited: false,
 
   init() {
     this._lastRecordTabCount = 0;
     urlbarListener.init();
@@ -336,18 +336,18 @@ let BrowserUsageTelemetry = {
     const counts = getOpenTabsAndWinsCounts();
     Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
     Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
 
     // Reset the URI counter.
     URICountListener.reset();
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   uninit() {
     if (!this._inited) {
       return;
     }
     Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC);
     Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC);
     urlbarListener.uninit();
--- a/browser/modules/FormSubmitObserver.jsm
+++ b/browser/modules/FormSubmitObserver.jsm
@@ -222,10 +222,10 @@ FormSubmitObserver.prototype =
     if (this._content == null) {
       return true;
     }
     let target = aEvent.originalTarget;
     return (target == this._content.document ||
             (target.ownerDocument && target.ownerDocument == this._content.document));
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver])
 };
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -264,17 +264,17 @@ var Sanitizer = {
         if (this.shouldSanitizeOnShutdown) {
           let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
           addPendingSanitization("shutdown", itemsToClear, {});
         }
       }
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsiObserver,
     Ci.nsISupportsWeakReference
   ]),
 
   // When making any changes to the sanitize implementations here,
   // please check whether the changes are applicable to Android
   // (mobile/android/modules/Sanitizer.jsm) as well.
 
--- a/browser/modules/SiteDataManager.jsm
+++ b/browser/modules/SiteDataManager.jsm
@@ -106,17 +106,17 @@ var SiteDataManager = {
       // Needs to root the observer since cache service keeps only a weak reference.
       this._getCacheSizeObserver = {
         onNetworkCacheDiskConsumption: consumption => {
           resolve(consumption);
           this._getCacheSizePromise = null;
           this._getCacheSizeObserver = null;
         },
 
-        QueryInterface: XPCOMUtils.generateQI([
+        QueryInterface: ChromeUtils.generateQI([
           Ci.nsICacheStorageConsumptionObserver,
           Ci.nsISupportsWeakReference
         ])
       };
 
       try {
         Services.cache2.asyncGetDiskConsumption(this._getCacheSizeObserver);
       } catch (e) {
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -150,17 +150,17 @@ function PreviewController(win, tab) {
   XPCOMUtils.defineLazyGetter(this, "canvasPreview", function() {
     let canvas = PageThumbs.createCanvas();
     canvas.mozOpaque = true;
     return canvas;
   });
 }
 
 PreviewController.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITaskbarPreviewController]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITaskbarPreviewController]),
 
   destroy() {
     this.tab.removeEventListener("TabAttrModified", this);
 
     // Break cycles, otherwise we end up leaking the window with everything
     // attached to it.
     delete this.win;
     delete this.preview;
@@ -830,17 +830,17 @@ var AeroPeek = {
           if (tab.getAttribute("image") == newValue) {
             win.onLinkIconAvailable(tab.linkedBrowser, newValue);
           }
         }
       }
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsISupportsWeakReference,
     Ci.nsINavHistoryObserver,
     Ci.nsIObserver
   ]),
 };
 
 XPCOMUtils.defineLazyGetter(AeroPeek, "cacheTimer", () =>
   Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
--- a/browser/modules/test/browser/browser_UsageTelemetry_domains.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_domains.js
@@ -26,17 +26,17 @@ function browserLocationChanged(browser)
       onStatusChange() {},
       onLocationChange(aWebProgress, aRequest, aURI, aFlags) {
         if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
           browser.webProgress.removeProgressListener(filter);
           filter.removeProgressListener(wpl);
           resolve();
         }
       },
-      QueryInterface: XPCOMUtils.generateQI([
+      QueryInterface: ChromeUtils.generateQI([
         Ci.nsIWebProgressListener,
         Ci.nsIWebProgressListener2,
       ]),
     };
     const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                      .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
--- a/browser/modules/test/browser/contentSearch.js
+++ b/browser/modules/test/browser/contentSearch.js
@@ -64,17 +64,17 @@ function waitForLoadAndStopIt(expectedUR
           if ((flags & docStart) && webProg.isTopLevel && url == expectedURL) {
             webProgress.removeProgressListener(webProgressListener);
             webProgressListener = null;
             req.cancel(Cr.NS_ERROR_FAILURE);
             resolve(url);
           }
         }
       },
-      QueryInterface: XPCOMUtils.generateQI([
+      QueryInterface: ChromeUtils.generateQI([
         Ci.nsIWebProgressListener,
         Ci.nsISupportsWeakReference,
       ]),
     };
     webProgress.addProgressListener(webProgressListener, Ci.nsIWebProgress.NOTIFY_ALL);
     dump("waitForLoadAndStopIt: Waiting for URL to load: " + expectedURL + "\n");
   });
 }
--- a/browser/modules/test/browser/formValidation/browser_form_validation.js
+++ b/browser/modules/test/browser/formValidation/browser_form_validation.js
@@ -44,17 +44,17 @@ function checkPopupShow() {
 }
 
 function checkPopupHide() {
   ok(gInvalidFormPopup.state != "showing" && gInvalidFormPopup.state != "open",
      "[Test " + testId + "] The invalid form popup should not be shown");
 }
 
 var gObserver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver]),
 
   notifyInvalidSubmit(aFormElement, aInvalidElements) {
   }
 };
 
 var testId = 0;
 
 function incrementTest() {
--- a/browser/modules/test/browser/head.js
+++ b/browser/modules/test/browser/head.js
@@ -165,33 +165,33 @@ function checkEvents(events, expectedEve
  * @param browser (<xul:browser>)
  *        The browser that we'll create a nsIContentPermissionRequest
  *        for.
  * @returns A nsIContentPermissionRequest-ish object.
  */
 function makeMockPermissionRequest(browser) {
   let type = {
     options: [],
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
   };
   let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
   types.appendElement(type);
   let result = {
     types,
     principal: browser.contentPrincipal,
     requester: null,
     _cancelled: false,
     cancel() {
       this._cancelled = true;
     },
     _allowed: false,
     allow() {
       this._allowed = true;
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
   };
 
   // In the e10s-case, nsIContentPermissionRequest will have
   // element defined. window is defined otherwise.
   if (browser.isRemoteBrowser) {
     result.element = browser;
   } else {
     result.window = browser.contentWindow;
--- a/chrome/test/unit/test_no_remote_registration.js
+++ b/chrome/test/unit/test_no_remote_registration.js
@@ -29,17 +29,17 @@ ProtocolHandler.prototype =
       mutator.setSpec(aBaseURI.resolve(aSpec));
     } else {
       mutator.setSpec(aSpec);
     }
     return mutator.finalize();
   },
   newChannel2() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
   newChannel() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIProtocolHandler
   ])
 };
 
 var testProtocols = [
   // It doesn't matter if it has this flag - the only flag we accept is
   // URI_IS_LOCAL_RESOURCE.
   {scheme: "moz-protocol-ui-resource",
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -484,59 +484,59 @@ class BoxModelMain extends PureComponent
       dom.div(
         {
           className: "boxmodel-box"
         },
         dom.span(
           {
             className: "boxmodel-legend",
             "data-box": "margin",
-            title: BOXMODEL_L10N.getStr("boxmodel.margin"),
+            title: "margin",
           },
-          BOXMODEL_L10N.getStr("boxmodel.margin")
+          "margin"
         ),
         dom.div(
           {
             className: "boxmodel-margins",
             "data-box": "margin",
-            title: BOXMODEL_L10N.getStr("boxmodel.margin"),
+            title: "margin",
             ref: div => {
               this.marginLayout = div;
             },
           },
           dom.span(
             {
               className: "boxmodel-legend",
               "data-box": "border",
-              title: BOXMODEL_L10N.getStr("boxmodel.border"),
+              title: "border",
             },
-            BOXMODEL_L10N.getStr("boxmodel.border")
+            "border"
           ),
           dom.div(
             {
               className: "boxmodel-borders",
               "data-box": "border",
-              title: BOXMODEL_L10N.getStr("boxmodel.border"),
+              title: "border",
               ref: div => {
                 this.borderLayout = div;
               },
             },
             dom.span(
               {
                 className: "boxmodel-legend",
                 "data-box": "padding",
-                title: BOXMODEL_L10N.getStr("boxmodel.padding"),
+                title: "padding",
               },
-              BOXMODEL_L10N.getStr("boxmodel.padding")
+              "padding"
             ),
             dom.div(
               {
                 className: "boxmodel-paddings",
                 "data-box": "padding",
-                title: BOXMODEL_L10N.getStr("boxmodel.padding"),
+                title: "padding",
                 ref: div => {
                   this.paddingLayout = div;
                 },
               },
               dom.div({
                 className: "boxmodel-contents",
                 "data-box": "content",
                 title: BOXMODEL_L10N.getStr("boxmodel.content"),
--- a/devtools/client/locales/en-US/boxmodel.properties
+++ b/devtools/client/locales/en-US/boxmodel.properties
@@ -14,28 +14,16 @@
 # LOCALIZATION NOTE (boxmodel.title) This is the title of the box model panel and is
 # displayed as a label.
 boxmodel.title=Box Model
 
 # LOCALIZATION NOTE (boxmodel.position) This refers to the position in the box model and
 # might be displayed as a label or as a tooltip.
 boxmodel.position=position
 
-# LOCALIZATION NOTE (boxmodel.margin) This refers to the margin in the box model and
-# might be displayed as a label or as a tooltip.
-boxmodel.margin=margin
-
-# LOCALIZATION NOTE (boxmodel.border) This refers to the border in the box model and
-# might be displayed as a label or as a tooltip.
-boxmodel.border=border
-
-# LOCALIZATION NOTE (boxmodel.padding) This refers to the padding in the box model and
-# might be displayed as a label or as a tooltip.
-boxmodel.padding=padding
-
 # LOCALIZATION NOTE (boxmodel.content) This refers to the content in the box model and
 # might be displayed as a label or as a tooltip.
 boxmodel.content=content
 
 # LOCALIZATION NOTE: (boxmodel.geometryButton.tooltip) This label is displayed as a
 # tooltip that appears when hovering over the button that allows users to edit the
 # position of an element in the page.
 boxmodel.geometryButton.tooltip=Edit position
--- a/devtools/client/responsive.html/browser/content.js
+++ b/devtools/client/responsive.html/browser/content.js
@@ -178,21 +178,15 @@ var global = this;
 
   let WebProgressListener = {
     onLocationChange(webProgress, request, URI, flags) {
       if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
         return;
       }
       makeScrollbarsFloating();
     },
-    QueryInterface: function QueryInterface(iid) {
-      if (iid.equals(Ci.nsIWebProgressListener) ||
-          iid.equals(Ci.nsISupportsWeakReference) ||
-          iid.equals(Ci.nsISupports)) {
-        return this;
-      }
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
+                                            "nsISupportsWeakReference"]),
   };
 })();
 
 global.responsiveFrameScriptLoaded = true;
 sendAsyncMessage("ResponsiveMode:ChildScriptReady");
--- a/devtools/client/responsive.html/browser/web-navigation.js
+++ b/devtools/client/responsive.html/browser/web-navigation.js
@@ -29,18 +29,17 @@ function readInputStreamToString(stream)
  */
 function BrowserElementWebNavigation(browser) {
   this._browser = browser;
 }
 
 BrowserElementWebNavigation.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIWebNavigation,
-    Ci.nsISupports
+    Ci.nsIWebNavigation
   ]),
 
   get _mm() {
     return this._browser.frameLoader.messageManager;
   },
 
   canGoBack: false,
   canGoForward: false,
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_requests_from_chrome.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_requests_from_chrome.js
@@ -9,17 +9,17 @@
 "use strict";
 
 const TEST_URI = "http://example.com/";
 
 add_task(async function() {
   // Start a listener on the console service.
   let good = true;
   const listener = {
-    QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ]),
+    QueryInterface: ChromeUtils.generateQI([ Ci.nsIObserver ]),
     observe: function(subject) {
       if (subject instanceof Ci.nsIScriptError &&
           subject.category === "XPConnect JavaScript" &&
           subject.sourceName.includes("webconsole")) {
         good = false;
       }
     }
   };
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_observer_notifications.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_observer_notifications.js
@@ -16,20 +16,18 @@ add_task(async function() {
   await openNewTabAndConsole(TEST_URI);
   await waitFor(() => created);
 
   await closeTabAndToolbox(gBrowser.selectedTab);
   await waitFor(() => destroyed);
 });
 
 function setupObserver() {
-  const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
-
   const observer = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
     observe: function observe(subject, topic) {
       subject = subject.QueryInterface(Ci.nsISupportsString);
 
       switch (topic) {
         case "web-console-created":
           ok(HUDService.getHudReferenceById(subject.data), "We have a hud reference");
           Services.obs.removeObserver(observer, "web-console-created");
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -576,18 +576,17 @@ HighlighterEnvironment.prototype = {
     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([
         Ci.nsIWebProgressListener,
-        Ci.nsISupportsWeakReference,
-        Ci.nsISupports
+        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;
         let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
 
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -403,17 +403,17 @@ const NodeActor = protocol.ActorClassWit
     let line = 0;
     let native = false;
     let override = listener.override || {};
     let tags = listener.tags || "";
     let type = listener.type || "";
     let url = "";
 
     // If the listener is an object with a 'handleEvent' method, use that.
-    if (listenerDO.class === "Object" || listenerDO.class === "XULElement") {
+    if (listenerDO.class === "Object" || /^XUL\w*Element$/.test(listenerDO.class)) {
       let desc;
 
       while (!desc && listenerDO) {
         desc = listenerDO.getOwnPropertyDescriptor("handleEvent");
         listenerDO = listenerDO.proto;
       }
 
       if (desc && desc.value) {
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -1503,17 +1503,16 @@ function DebuggerProgressListener(tabAct
 
   this._watchedDocShells = new WeakSet();
 }
 
 DebuggerProgressListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
-    Ci.nsISupports,
   ]),
 
   destroy() {
     Services.obs.removeObserver(this, "inner-window-destroyed");
     this._knownWindowIDs.clear();
     this._knownWindowIDs = null;
   },
 
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1141,17 +1141,17 @@ const ThreadActor = ActorClassWithSpec(t
           listenerForm.isEventHandler = !!node.hasAttribute(handlerName);
         }
         if (node[handlerName]) {
           listenerForm.isEventHandler = !!node[handlerName];
         }
         // Get the Debugger.Object for the listener object.
         let listenerDO = this.globalDebugObject.makeDebuggeeValue(listener);
         // If the listener is an object with a 'handleEvent' method, use that.
-        if (listenerDO.class == "Object" || listenerDO.class == "XULElement") {
+        if (listenerDO.class === "Object" || /^XUL\w*Element$/.test(listenerDO.class)) {
           // For some events we don't have permission to access the
           // 'handleEvent' property when running in content scope.
           if (!listenerDO.unwrap()) {
             continue;
           }
           let heDesc;
           while (!heDesc && listenerDO) {
             heDesc = listenerDO.getOwnPropertyDescriptor("handleEvent");
--- a/devtools/server/actors/webextension-inspected-window.js
+++ b/devtools/server/actors/webextension-inspected-window.js
@@ -108,18 +108,17 @@ function CustomizedReload(params) {
   this.injectedScript = params.injectedScript;
   this.userAgent = params.userAgent;
 
   this.customizedReloadWindows = new WeakSet();
 }
 
 CustomizedReload.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISupports]),
+                                         Ci.nsISupportsWeakReference]),
   get window() {
     return this.docShell.DOMWindow;
   },
 
   get webNavigation() {
     return this.docShell
                .QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIWebNavigation);
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -486,25 +486,19 @@ function DownloadListener(win, transfer)
   }
 
   // Allow saveToFile to await completion for error handling
   this._completedDeferred = defer();
   this.completed = this._completedDeferred.promise;
 }
 
 DownloadListener.prototype = {
-  QueryInterface: function(iid) {
-    if (iid.equals(Ci.nsIInterfaceRequestor) ||
-        iid.equals(Ci.nsIWebProgressListener) ||
-        iid.equals(Ci.nsIWebProgressListener2) ||
-        iid.equals(Ci.nsISupports)) {
-      return this;
-    }
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor",
+                                          "nsIWebProgressListener",
+                                          "nsIWebProgressListener2"]),
 
   getInterface: function(iid) {
     if (iid.equals(Ci.nsIAuthPrompt) ||
         iid.equals(Ci.nsIAuthPrompt2)) {
       let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                  .getService(Ci.nsIPromptFactory);
       return ww.getPrompt(this.window, iid);
     }
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -281,18 +281,17 @@ function NetworkResponseListener(owner, 
   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,
-                           Ci.nsISupports]),
+                           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) {
--- a/devtools/shared/webconsole/throttle.js
+++ b/devtools/shared/webconsole/throttle.js
@@ -37,18 +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,
-                           Ci.nsISupports]),
+    XPCOMUtils.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/aboutdebugging-registration.js
+++ b/devtools/startup/aboutdebugging-registration.js
@@ -16,17 +16,17 @@ const { nsIAboutModule } = Ci;
 function AboutDebugging() {}
 
 AboutDebugging.prototype = {
   uri: Services.io.newURI("chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml"),
   classDescription: "about:debugging",
   classID: Components.ID("1060afaf-dc9e-43da-8646-23a2faf48493"),
   contractID: "@mozilla.org/network/protocol/about;1?what=debugging",
 
-  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
+  QueryInterface: ChromeUtils.generateQI([nsIAboutModule]),
 
   newChannel: function(uri, loadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(
       this.uri,
       loadInfo
     );
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
     return chan;
--- a/devtools/startup/aboutdevtools/aboutdevtools-registration.js
+++ b/devtools/startup/aboutdevtools/aboutdevtools-registration.js
@@ -14,17 +14,17 @@ const { nsIAboutModule } = Ci;
 function AboutDevtools() {}
 
 AboutDevtools.prototype = {
   uri: Services.io.newURI("chrome://devtools-startup/content/aboutdevtools/aboutdevtools.xhtml"),
   classDescription: "about:devtools",
   classID: Components.ID("3a16d383-92bd-4c24-ac10-0e2bd66883ab"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools",
 
-  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
+  QueryInterface: ChromeUtils.generateQI([nsIAboutModule]),
 
   newChannel: function(uri, loadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(
       this.uri,
       loadInfo
     );
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
     return chan;
--- a/devtools/startup/aboutdevtoolstoolbox-registration.js
+++ b/devtools/startup/aboutdevtoolstoolbox-registration.js
@@ -15,17 +15,17 @@ const { nsIAboutModule } = Ci;
 function AboutDevtoolsToolbox() {}
 
 AboutDevtoolsToolbox.prototype = {
   uri: Services.io.newURI("chrome://devtools/content/framework/toolbox.xul"),
   classDescription: "about:devtools-toolbox",
   classID: Components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",
 
-  QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
+  QueryInterface: ChromeUtils.generateQI([nsIAboutModule]),
 
   newChannel: function(uri, loadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, loadInfo);
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
     return chan;
   },
 
   getURIFlags: function(uri) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9589,19 +9589,21 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       }
       // If we are a noopener load, we just hand the whole thing over to our
       // window.
       if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
         // Various asserts that we know to hold because NO_OPENER loads can only
         // happen for links.
         MOZ_ASSERT(!aLoadReplace);
         MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal);
-        MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
-                   aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
-                              INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
+        MOZ_ASSERT((aFlags & ~INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED) ==
+                   INTERNAL_LOAD_FLAGS_NO_OPENER ||
+                   (aFlags & ~INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED) ==
+                   (INTERNAL_LOAD_FLAGS_NO_OPENER |
+                    INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
         MOZ_ASSERT(!aPostData);
         MOZ_ASSERT(!aHeadersData);
         // If OnLinkClickSync was invoked inside the onload handler, the load
         // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
         // LOAD_LINK.
         MOZ_ASSERT(aLoadType == LOAD_LINK ||
                    aLoadType == LOAD_NORMAL_REPLACE);
         MOZ_ASSERT(!aSHEntry);
@@ -13353,16 +13355,17 @@ public:
   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
                    nsIURI* aURI,
                    const char16_t* aTargetSpec,
                    const nsAString& aFileName,
                    nsIInputStream* aPostDataStream,
                    int64_t aPostDataStreamLength,
                    nsIInputStream* aHeadersDataStream,
                    bool aNoOpenerImplied,
+                   bool aIsUserTriggered,
                    bool aIsTrusted,
                    nsIPrincipal* aTriggeringPrincipal);
 
   NS_IMETHOD Run() override
   {
     nsAutoPopupStatePusher popupStatePusher(mPopupState);
 
     // We need to set up an AutoJSAPI here for the following reason: When we do
@@ -13371,71 +13374,76 @@ public:
     // So we need to fake things so that we don't look like native code as far
     // as LegacyIsCallerNativeCode() is concerned.
     AutoJSAPI jsapi;
     if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
       mHandler->OnLinkClickSync(mContent, mURI,
                                 mTargetSpec.get(), mFileName,
                                 mPostDataStream, mPostDataStreamLength,
                                 mHeadersDataStream, mNoOpenerImplied,
-                                nullptr, nullptr, mTriggeringPrincipal);
+                                nullptr, nullptr, mIsUserTriggered,
+                                mTriggeringPrincipal);
     }
     return NS_OK;
   }
 
 private:
   RefPtr<nsDocShell> mHandler;
   nsCOMPtr<nsIURI> mURI;
   nsString mTargetSpec;
   nsString mFileName;
   nsCOMPtr<nsIInputStream> mPostDataStream;
   int64_t mPostDataStreamLength;
   nsCOMPtr<nsIInputStream> mHeadersDataStream;
   nsCOMPtr<nsIContent> mContent;
   PopupControlState mPopupState;
   bool mNoOpenerImplied;
+  bool mIsUserTriggered;
   bool mIsTrusted;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
 };
 
 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
                                    nsIContent* aContent,
                                    nsIURI* aURI,
                                    const char16_t* aTargetSpec,
                                    const nsAString& aFileName,
                                    nsIInputStream* aPostDataStream,
                                    int64_t aPostDataStreamLength,
                                    nsIInputStream* aHeadersDataStream,
                                    bool aNoOpenerImplied,
+                                   bool aIsUserTriggered,
                                    bool aIsTrusted,
                                    nsIPrincipal* aTriggeringPrincipal)
   : mozilla::Runnable("OnLinkClickEvent")
   , mHandler(aHandler)
   , mURI(aURI)
   , mTargetSpec(aTargetSpec)
   , mFileName(aFileName)
   , mPostDataStream(aPostDataStream)
   , mPostDataStreamLength(aPostDataStreamLength)
   , mHeadersDataStream(aHeadersDataStream)
   , mContent(aContent)
   , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
   , mNoOpenerImplied(aNoOpenerImplied)
+  , mIsUserTriggered(aIsUserTriggered)
   , mIsTrusted(aIsTrusted)
   , mTriggeringPrincipal(aTriggeringPrincipal)
 {
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const char16_t* aTargetSpec,
                         const nsAString& aFileName,
                         nsIInputStream* aPostDataStream,
                         int64_t aPostDataStreamLength,
                         nsIInputStream* aHeadersDataStream,
+                        bool aIsUserTriggered,
                         bool aIsTrusted,
                         nsIPrincipal* aTriggeringPrincipal)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
     return NS_OK;
   }
@@ -13472,17 +13480,17 @@ nsDocShell::OnLinkClick(nsIContent* aCon
   if (NS_FAILED(rv)) {
     target = aTargetSpec;
   }
 
   nsCOMPtr<nsIRunnable> ev =
     new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
                          aPostDataStream, aPostDataStreamLength,
                          aHeadersDataStream, noOpenerImplied,
-                         aIsTrusted, aTriggeringPrincipal);
+                         aIsUserTriggered, aIsTrusted, aTriggeringPrincipal);
   return DispatchToTabGroup(TaskCategory::UI, ev.forget());
 }
 
 static bool
 IsElementAnchorOrArea(nsIContent* aContent)
 {
   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
   // or XHTML namespace.
@@ -13495,16 +13503,17 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                             const char16_t* aTargetSpec,
                             const nsAString& aFileName,
                             nsIInputStream* aPostDataStream,
                             int64_t aPostDataStreamLength,
                             nsIInputStream* aHeadersDataStream,
                             bool aNoOpenerImplied,
                             nsIDocShell** aDocShell,
                             nsIRequest** aRequest,
+                            bool aIsUserTriggered,
                             nsIPrincipal* aTriggeringPrincipal)
 {
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nullptr;
   }
   if (aRequest) {
     *aRequest = nullptr;
@@ -13633,16 +13642,20 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                          : aContent->NodePrincipal();
 
   // Link click (or form submission) can be triggered inside an onload handler,
   // and we don't want to add history entry in this case.
   bool inOnLoadHandler = false;
   GetIsExecutingOnLoadHandler(&inOnLoadHandler);
   uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
 
+  if (aIsUserTriggered) {
+    flags |= INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED;
+  }
+
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              nullptr,                   // Original URI
                              Nothing(),                 // Let the protocol handler assign it
                              false,                     // LoadReplace
                              referer,                   // Referer URI
                              refererPolicy,             // Referer policy
                              triggeringPrincipal,
                              aContent->NodePrincipal(),
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -205,28 +205,30 @@ public:
   // nsILinkHandler
   NS_IMETHOD OnLinkClick(nsIContent* aContent,
                          nsIURI* aURI,
                          const char16_t* aTargetSpec,
                          const nsAString& aFileName,
                          nsIInputStream* aPostDataStream,
                          int64_t aPostDataStreamLength,
                          nsIInputStream* aHeadersDataStream,
+                         bool aIsUserTriggered,
                          bool aIsTrusted,
                          nsIPrincipal* aTriggeringPrincipal) override;
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
                              int64_t aPostDataStreamLength = -1,
                              nsIInputStream* aHeadersDataStream = 0,
                              bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0,
+                             bool aIsUserTriggered = false,
                              nsIPrincipal* aTriggeringPrincipal = nullptr) override;
   NS_IMETHOD OnOverLink(nsIContent* aContent,
                         nsIURI* aURI,
                         const char16_t* aTargetSpec) override;
   NS_IMETHOD OnLeaveLink() override;
 
   // Don't use NS_DECL_NSILOADCONTEXT because some of nsILoadContext's methods
   // are shared with nsIDocShell (appID, etc.) and can't be declared twice.
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -134,16 +134,19 @@ interface nsIDocShell : nsIDocShellTreeI
   // Whether this is the load of a frame's original src attribute
   const long INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC      = 0x80;
 
   const long INTERNAL_LOAD_FLAGS_NO_OPENER               = 0x100;
 
   // Whether a top-level data URI navigation is allowed for that load
   const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI    = 0x200;
 
+  // Whether the load was triggered by user interaction.
+  const long INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED       = 0x1000;
+
   /**
    * Loads the given URI.  This method is identical to loadURI(...) except
    * that its parameter list is broken out instead of being packaged inside
    * of an nsIDocShellLoadInfo object...
    *
    * @param aURI                 - The URI to load.
    * @param aOriginalURI         - The URI to set as the originalURI on the channel
    *                               that does the load. If null, aURI will be set as
--- a/docshell/base/nsILinkHandler.h
+++ b/docshell/base/nsILinkHandler.h
@@ -44,16 +44,17 @@ public:
    */
   NS_IMETHOD OnLinkClick(nsIContent* aContent,
                          nsIURI* aURI,
                          const char16_t* aTargetSpec,
                          const nsAString& aFileName,
                          nsIInputStream* aPostDataStream,
                          int64_t aPostDataStreamLength,
                          nsIInputStream* aHeadersDataStream,
+                         bool aIsUserTriggered,
                          bool aIsTrusted,
                          nsIPrincipal* aTriggeringPrincipal) = 0;
 
   /**
    * Process a click on a link.
    *
    * Works the same as OnLinkClick() except it happens immediately rather than
    * through an event.
@@ -77,16 +78,17 @@ public:
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
                              int64_t aPostDataStreamLength = -1,
                              nsIInputStream* aHeadersDataStream = 0,
                              bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0,
+                             bool aIsUserTriggered = false,
                              nsIPrincipal* aTriggeringPrincipal = nullptr) = 0;
 
   /**
    * Process a mouse-over a link.
    *
    * @param aContent the linked content.
    * @param aURI an URI object that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (it may be an empty
--- a/docshell/test/browser/browser_bug655270.js
+++ b/docshell/test/browser/browser_bug655270.js
@@ -48,13 +48,13 @@ function test() {
 
     onBeginUpdateBatch: function() { },
     onEndUpdateBatch: function() { },
     onVisits: function() { },
     onTitleChanged: function() { },
     onDeleteURI: function() { },
     onClearHistory: function() { },
     onDeleteVisits: function() { },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
+    QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryObserver])
   };
 
   PlacesUtils.history.addObserver(observer);
 }
--- a/docshell/test/browser/browser_bug670318.js
+++ b/docshell/test/browser/browser_bug670318.js
@@ -46,18 +46,18 @@ add_task(async function test() {
         OnHistoryReplaceEntry: () => {
           // The initial load of about:blank causes a transient entry to be
           // created, so our first navigation to a real page is a replace
           // instead of a new entry.
           ++count;
           return true;
         },
 
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsISHistoryListener,
-                                               Ci.nsISupportsWeakReference])
+        QueryInterface: ChromeUtils.generateQI([Ci.nsISHistoryListener,
+                                                Ci.nsISupportsWeakReference])
       };
 
       history.legacySHistory.addSHistoryListener(listener);
       // Since listener implements nsISupportsWeakReference, we are
       // responsible for keeping it alive so that the GC doesn't clear
       // it before the test completes. We do this by anchoring the listener
       // to the content global window, and clearing it just before the test
       // completes.
--- a/docshell/test/browser/browser_onbeforeunload_navigation.js
+++ b/docshell/test/browser/browser_onbeforeunload_navigation.js
@@ -150,17 +150,17 @@ var tabStateListener = {
     if ((stateFlags & startDocumentFlags) == startDocumentFlags) {
       loadStarted = true;
     }
   },
   onStatusChange: () => {},
   onLocationChange: () => {},
   onSecurityChange: () => {},
   onProgressChange: () => {},
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener])
 };
 
 function onTabModalDialogLoaded(node) {
   let mm = testTab.linkedBrowser.messageManager;
   mm.sendAsyncMessage("test-beforeunload:dialog");
 
   if (gMultiProcessBrowser) {
     // In non-e10s, onTabModalDialogLoaded fires synchronously while
--- a/docshell/test/browser/file_bug1328501_framescript.js
+++ b/docshell/test/browser/file_bug1328501_framescript.js
@@ -9,17 +9,17 @@ let requestObserver = {
       // content viewers being created.
       getChildDocShells().map(ds => {
         let window = ds.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsILoadContext)
                       .associatedWindow;
       });
     }
   },
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver
   ])
 }
 Services.obs.addObserver(requestObserver, "http-on-opening-request");
 addEventListener("unload", e => {
   if (e.target == this) {
     Services.obs.removeObserver(requestObserver, "http-on-opening-request");
   }
--- a/docshell/test/browser/file_bug422543_script.js
+++ b/docshell/test/browser/file_bug422543_script.js
@@ -33,18 +33,18 @@ SHistoryListener.prototype = {
 
   OnHistoryReload: function (aReloadURI, aReloadFlags) {
     this.last = "reload";
     return this.retval;
   },
 
   OnHistoryReplaceEntry: function (aIndex) {},
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISHistoryListener,
-                                         Ci.nsISupportsWeakReference])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISHistoryListener,
+                                          Ci.nsISupportsWeakReference])
 };
 
 let testAPI = {
   shistory: null,
   listeners: [ new SHistoryListener(),
                new SHistoryListener() ],
 
   init() {
--- a/docshell/test/chrome/test_bug909218.html
+++ b/docshell/test/chrome/test_bug909218.html
@@ -104,17 +104,17 @@ RequestWatcher = {
     is(req.loadFlags & TEST_FLAGS, TEST_FLAGS, "request " + req.name + " has the expected flags");
     this.requestCounts[req.name] += 1;
     var stopFlags = Ci.nsIWebProgressListener.STATE_STOP |
                     Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
     if (req.name == TEST_URL && (flags & stopFlags) == stopFlags) {
       this.finalize();
     }
   },
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
   ])
 }
 
 function docshellForWindow(win) {
   return win.
          QueryInterface(Ci.nsIInterfaceRequestor).
--- a/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
+++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
@@ -54,18 +54,18 @@ https://bugzilla.mozilla.org/show_bug.cg
             reject("no network error message found in URI")
             return;
           }
 
           var errorMsg = matchArray[1];
           resolve(decodeURIComponent(errorMsg));
         },
 
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                               Ci.nsISupportsWeakReference])
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                                Ci.nsISupportsWeakReference])
       };
 
       frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebProgress)
                          .addProgressListener(progressListener,
                                               Ci.nsIWebProgress.NOTIFY_LOCATION |
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -37,18 +37,18 @@ function DOMRequestIpcHelper() {
   this._window = null;
 }
 
 DOMRequestIpcHelper.prototype = {
   /**
    * An object which "inherits" from DOMRequestIpcHelper and declares its own
    * queryInterface method MUST implement Ci.nsISupportsWeakReference.
    */
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference,
+                                          Ci.nsIObserver]),
 
    /**
    *  'aMessages' is expected to be an array of either:
    *  - objects of this form:
    *    {
    *      name: "messageName",
    *      weakRef: false
    *    }
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -1449,21 +1449,17 @@ EventSourceImpl::DispatchCurrentMessageE
   if (message->mEventName.IsEmpty()) {
     message->mEventName.AssignLiteral("message");
   }
 
   if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
     message->mLastEventID.Assign(mLastEventID);
   }
 
-  size_t sizeBefore = mMessagesToDispatch.GetSize();
   mMessagesToDispatch.Push(message.release());
-  NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
-                 NS_ERROR_OUT_OF_MEMORY);
-
 
   if (!mGoingToDispatchAllMessages) {
     nsCOMPtr<nsIRunnable> event =
       NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
                         this,
                         &EventSourceImpl::DispatchAllMessageEvents);
     NS_ENSURE_STATE(event);
 
--- a/dom/base/ProcessSelector.js
+++ b/dom/base/ProcessSelector.js
@@ -7,17 +7,17 @@ ChromeUtils.import('resource://gre/modul
 
 // Fills up aProcesses until max and then selects randomly from the available
 // ones.
 function RandomSelector() {
 }
 
 RandomSelector.prototype = {
   classID:          Components.ID("{c616fcfd-9737-41f1-aa74-cee72a38f91b}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
+  QueryInterface:   ChromeUtils.generateQI([Ci.nsIContentProcessProvider]),
 
   provideProcess(aType, aOpener, aProcesses, aCount, aMaxCount) {
     if (aCount < aMaxCount) {
       return Ci.nsIContentProcessProvider.NEW_PROCESS;
     }
 
     let startIdx = Math.floor(Math.random() * aMaxCount);
     let curIdx = startIdx;
@@ -36,17 +36,17 @@ RandomSelector.prototype = {
 
 // Fills up aProcesses until max and then selects one from the available
 // ones that host the least number of tabs.
 function MinTabSelector() {
 }
 
 MinTabSelector.prototype = {
   classID:          Components.ID("{2dc08eaf-6eef-4394-b1df-a3a927c1290b}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
+  QueryInterface:   ChromeUtils.generateQI([Ci.nsIContentProcessProvider]),
 
   provideProcess(aType, aOpener, aProcesses, aCount, aMaxCount) {
     if (aCount < aMaxCount) {
       return Ci.nsIContentProcessProvider.NEW_PROCESS;
     }
 
     let min = Number.MAX_VALUE;
     let candidate = Ci.nsIContentProcessProvider.NEW_PROCESS;
--- a/dom/base/SlowScriptDebug.js
+++ b/dom/base/SlowScriptDebug.js
@@ -7,17 +7,17 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function SlowScriptDebug() { }
 
 SlowScriptDebug.prototype = {
   classID: Components.ID("{e740ddb4-18b4-4aac-8ae1-9b0f4320769d}"),
   classDescription: "Slow script debug handler",
   contractID: "@mozilla.org/dom/slow-script-debug;1",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISlowScriptDebug]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISlowScriptDebug]),
 
   get activationHandler()   { return this._activationHandler; },
   set activationHandler(cb) { return this._activationHandler = cb; },
 
   get remoteActivationHandler()   { return this._remoteActivationHandler; },
   set remoteActivationHandler(cb) { return this._remoteActivationHandler = cb; },
 };
 
--- a/dom/base/contentAreaDropListener.js
+++ b/dom/base/contentAreaDropListener.js
@@ -13,17 +13,17 @@ ChromeUtils.import("resource://gre/modul
 // access the uri. This prevents, for example, a source document from tricking
 // the user into dragging a chrome url.
 
 function ContentAreaDropListener() { };
 
 ContentAreaDropListener.prototype =
 {
   classID:          Components.ID("{1f34bc80-1bc7-11d6-a384-d705dd0746fc}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDroppedLinkHandler, Ci.nsISupports]),
+  QueryInterface:   ChromeUtils.generateQI([Ci.nsIDroppedLinkHandler]),
 
   _addLink : function(links, url, name, type)
    {
     links.push({ url, name, type });
   },
 
   _addLinksFromItem: function(links, dt, i)
   {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5539,18 +5539,18 @@ nsContentUtils::TriggerLink(nsIContent *
          !aContent->IsSVGElement(nsGkAtoms::a)) ||
         !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
         NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
       fileName.SetIsVoid(true); // No actionable download attribute was found.
     }
 
     handler->OnLinkClick(aContent, aLinkURI,
                          fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
-                         fileName, nullptr, -1, nullptr, aIsTrusted,
-                         aContent->NodePrincipal());
+                         fileName, nullptr, -1, nullptr, EventStateManager::IsHandlingUserInput(),
+                         aIsTrusted, aContent->NodePrincipal());
   }
 }
 
 /* static */
 void
 nsContentUtils::GetLinkLocation(Element* aElement, nsString& aLocationString)
 {
   nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
@@ -6688,19 +6688,17 @@ nsContentUtils::IsFocusedContent(const n
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 
   return fm && fm->GetFocusedElement() == aContent;
 }
 
 bool
 nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
 {
-  //XXXsmaug Shadow DOM spec issue!
-  //         We may need to change this to GetComposedDoc().
-  nsIDocument* doc = aContent->GetUncomposedDoc();
+  nsIDocument* doc = aContent->GetComposedDoc();
   if (!doc) {
     return false;
   }
 
   // If the subdocument lives in another process, the frame is
   // tabbable.
   if (EventStateManager::IsRemoteTarget(aContent)) {
     return true;
@@ -9992,43 +9990,43 @@ nsContentUtils::NewXULOrHTMLElement(Elem
     // Step 6.1.
     if (synchronousCustomElements) {
       DoCustomElementCreate(aResult, nodeInfo->GetDocument(), nodeInfo,
                             definition->mConstructor, rv);
       if (rv.MaybeSetPendingException(cx)) {
         if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
           NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
         } else {
-          NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+          NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
         }
       }
       return NS_OK;
     }
 
     // Step 6.2.
     if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
       NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     } else {
-      NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+      NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
     }
     (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
     nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
     return NS_OK;
   }
 
   if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
     // Per the Custom Element specification, unknown tags that are valid custom
     // element names should be HTMLElement instead of HTMLUnknownElement.
     if (isCustomElementName) {
       NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     } else {
       *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
     }
   } else {
-    NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+    NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
   }
 
   if (!*aResult) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (customElementEnabled && isCustomElement) {
     (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -292,16 +292,43 @@ nsDOMNavigationTiming::NotifyNonBlankPai
 
     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
                                    mNavigationStart,
                                    mNonBlankPaint);
   }
 }
 
 void
+nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mNavigationStart.IsNull());
+
+  if (!mDOMContentFlushed.IsNull()) {
+    return;
+  }
+
+  mDOMContentFlushed = TimeStamp::Now();
+
+#ifdef MOZ_GECKO_PROFILER
+  if (profiler_is_active()) {
+    TimeDuration elapsed = mDOMContentFlushed - mNavigationStart;
+    nsAutoCString spec;
+    if (mLoadedURI) {
+      mLoadedURI->GetSpec(spec);
+    }
+    nsPrintfCString marker("DOMContentFlushed after %dms for URL %s, %s",
+                           int(elapsed.ToMilliseconds()), spec.get(),
+                           mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start and DOMContentFlushed");
+    profiler_add_marker(marker.get());
+  }
+#endif
+}
+
+void
 nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState)
 {
   mDocShellHasBeenActiveSinceNavigationStart &=
     (aDocShellState == DocShellState::eActive);
 }
 
 mozilla::TimeStamp
 nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const
--- a/dom/base/nsDOMNavigationTiming.h
+++ b/dom/base/nsDOMNavigationTiming.h
@@ -91,16 +91,20 @@ public:
   DOMTimeMilliSec GetLoadEventEnd() const
   {
     return TimeStampToDOM(mLoadEventEnd);
   }
   DOMTimeMilliSec GetTimeToNonBlankPaint() const
   {
     return TimeStampToDOM(mNonBlankPaint);
   }
+  DOMTimeMilliSec GetTimeToDOMContentFlushed() const
+  {
+    return TimeStampToDOM(mDOMContentFlushed);
+  }
 
   DOMHighResTimeStamp GetUnloadEventStartHighRes()
   {
     mozilla::TimeStamp stamp = GetUnloadEventStartTimeStamp();
     if (stamp.IsNull()) {
       return 0;
     }
     return TimeStampToDOMHighRes(stamp);
@@ -156,16 +160,17 @@ public:
   void SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue);
   void NotifyDOMLoading(nsIURI* aURI);
   void NotifyDOMInteractive(nsIURI* aURI);
   void NotifyDOMComplete(nsIURI* aURI);
   void NotifyDOMContentLoadedStart(nsIURI* aURI);
   void NotifyDOMContentLoadedEnd(nsIURI* aURI);
 
   void NotifyNonBlankPaintForRootContentDocument();
+  void NotifyDOMContentFlushedForRootContentDocument();
   void NotifyDocShellStateChanged(DocShellState aDocShellState);
 
   DOMTimeMilliSec TimeStampToDOM(mozilla::TimeStamp aStamp) const;
 
   inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp) const
   {
     if (aStamp.IsNull()) {
       return 0;
@@ -189,16 +194,17 @@ private:
 
   nsCOMPtr<nsIURI> mUnloadedURI;
   nsCOMPtr<nsIURI> mLoadedURI;
 
   Type mNavigationType;
   DOMHighResTimeStamp mNavigationStartHighRes;
   mozilla::TimeStamp mNavigationStart;
   mozilla::TimeStamp mNonBlankPaint;
+  mozilla::TimeStamp mDOMContentFlushed;
 
   mozilla::TimeStamp mBeforeUnloadStart;
   mozilla::TimeStamp mUnloadStart;
   mozilla::TimeStamp mUnloadEnd;
   mozilla::TimeStamp mLoadEventStart;
   mozilla::TimeStamp mLoadEventEnd;
 
   mozilla::TimeStamp mDOMLoading;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -251,17 +251,16 @@
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #ifdef MOZ_XUL
 #include "mozilla/dom/ListBoxObject.h"
 #include "mozilla/dom/MenuBoxObject.h"
-#include "mozilla/dom/PopupBoxObject.h"
 #include "mozilla/dom/ScrollBoxObject.h"
 #include "mozilla/dom/TreeBoxObject.h"
 #endif
 #include "nsIPresShellInlines.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
@@ -5460,16 +5459,20 @@ nsIDocument::UnblockDOMContentLoaded()
   MOZ_ASSERT(mBlockDOMContentLoaded);
   if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
     return;
   }
 
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p UnblockDOMContentLoaded", this));
 
   mDidFireDOMContentLoaded = true;
+  if (nsIPresShell* shell = GetShell()) {
+    shell->GetRefreshDriver()->NotifyDOMContentLoaded();
+  }
+
 
   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
   if (!mSynchronousDOMContentLoaded) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod("nsIDocument::DispatchContentLoadedEvents",
                         this,
                         &nsIDocument::DispatchContentLoadedEvents);
@@ -6571,21 +6574,16 @@ nsIDocument::GetBoxObjectFor(Element* aE
   }
 
   int32_t namespaceID;
   RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
 #ifdef MOZ_XUL
   if (namespaceID == kNameSpaceID_XUL) {
     if (tag == nsGkAtoms::menu) {
       boxObject = new MenuBoxObject();
-    } else if (tag == nsGkAtoms::popup ||
-               tag == nsGkAtoms::menupopup ||
-               tag == nsGkAtoms::panel ||
-               tag == nsGkAtoms::tooltip) {
-      boxObject = new PopupBoxObject();
     } else if (tag == nsGkAtoms::tree) {
       boxObject = new TreeBoxObject();
     } else if (tag == nsGkAtoms::listbox) {
       boxObject = new ListBoxObject();
     } else if (tag == nsGkAtoms::scrollbox) {
       boxObject = new ScrollBoxObject();
     } else {
       boxObject = new BoxObject();
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3234,19 +3234,21 @@ nsFocusManager::HostOrSlotTabIndexValue(
   }
 
   return -1;
 }
 
 nsIContent*
 nsFocusManager::GetNextTabbableContentInScope(nsIContent* aOwner,
                                               nsIContent* aStartContent,
+                                              nsIContent* aOriginalStartContent,
                                               bool aForward,
                                               int32_t aCurrentTabIndex,
                                               bool aIgnoreTabIndex,
+                                              bool aForDocumentNavigation,
                                               bool aSkipOwner)
 {
   // Return shadow host at first for forward navigation if its tabindex
   // is non-negative
   bool skipOwner = aSkipOwner || !aOwner->GetShadowRoot();
   if (!skipOwner && (aForward && aOwner == aStartContent)) {
     int32_t tabIndex = 0;
     aOwner->IsFocusable(&tabIndex);
@@ -3356,24 +3358,43 @@ nsFocusManager::GetNextTabbableContentIn
             }
           }
         }
 
         continue;
       }
 
       if (!IsHostOrSlot(iterContent)) {
+        nsCOMPtr<nsIContent> elementInFrame;
+        bool checkSubDocument = true;
+        if (aForDocumentNavigation &&
+            TryDocumentNavigation(iterContent, &checkSubDocument,
+                                  getter_AddRefs(elementInFrame))) {
+          return elementInFrame;
+        }
+        if (!checkSubDocument) {
+          continue;
+        }
+
+        if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
+                                        aForward, aForDocumentNavigation,
+                                        getter_AddRefs(elementInFrame))) {
+          return elementInFrame;
+        }
+
         // Found content to focus
         return iterContent;
       }
 
       // Search in scope owned by iterContent
       nsIContent* contentToFocus =
-        GetNextTabbableContentInScope(iterContent, iterContent, aForward,
+        GetNextTabbableContentInScope(iterContent, iterContent,
+                                      aOriginalStartContent, aForward,
                                       aForward ? 1 : 0, aIgnoreTabIndex,
+                                      aForDocumentNavigation,
                                       false /* aSkipOwner */);
       if (contentToFocus) {
         return contentToFocus;
       }
     };
 
     // If already at lowest priority tab (0), end search completely.
     // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
@@ -3397,30 +3418,33 @@ nsFocusManager::GetNextTabbableContentIn
   }
 
   return nullptr;
 }
 
 nsIContent*
 nsFocusManager::GetNextTabbableContentInAncestorScopes(
   nsIContent** aStartContent,
+  nsIContent* aOriginalStartContent,
   bool aForward,
   int32_t* aCurrentTabIndex,
-  bool aIgnoreTabIndex)
+  bool aIgnoreTabIndex,
+  bool aForDocumentNavigation)
 {
   nsIContent* startContent = *aStartContent;
   while (1) {
     nsIContent* owner = FindOwner(startContent);
     MOZ_ASSERT(owner, "focus navigation scope owner not in document");
 
     int32_t tabIndex = 0;
     startContent->IsFocusable(&tabIndex);
     nsIContent* contentToFocus =
-      GetNextTabbableContentInScope(owner, startContent, aForward,
-                                    tabIndex, aIgnoreTabIndex,
+      GetNextTabbableContentInScope(owner, startContent, aOriginalStartContent,
+                                    aForward, tabIndex, aIgnoreTabIndex,
+                                    aForDocumentNavigation,
                                     false /* aSkipOwner */);
     if (contentToFocus) {
       return contentToFocus;
     }
 
     // If not found in shadow DOM, search from the shadow host in light DOM
     if (!owner->IsInShadowTree()) {
       MOZ_ASSERT(owner->GetShadowRoot());
@@ -3456,33 +3480,38 @@ nsFocusManager::GetNextTabbableContent(n
   LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
   LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
 
   if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
     // If aStartContent is a shadow host or slot in forward navigation,
     // search in scope owned by aStartContent
     if (aForward && IsHostOrSlot(aStartContent)) {
       nsIContent* contentToFocus =
-        GetNextTabbableContentInScope(aStartContent, aStartContent, aForward,
+        GetNextTabbableContentInScope(aStartContent, aStartContent,
+                                      aOriginalStartContent, aForward,
                                       aForward ? 1 : 0, aIgnoreTabIndex,
+                                      aForDocumentNavigation,
                                       true /* aSkipOwner */);
       if (contentToFocus) {
         NS_ADDREF(*aResultContent = contentToFocus);
         return NS_OK;
       }
     }
 
     // If aStartContent is not in scope owned by aRootContent
     // (e.g., aStartContent is already in shadow DOM),
     // search from scope including aStartContent
     if (aRootContent != FindOwner(aStartContent)) {
       nsIContent* contentToFocus =
-        GetNextTabbableContentInAncestorScopes(&aStartContent, aForward,
+        GetNextTabbableContentInAncestorScopes(&aStartContent,
+                                               aOriginalStartContent,
+                                               aForward,
                                                &aCurrentTabIndex,
-                                               aIgnoreTabIndex);
+                                               aIgnoreTabIndex,
+                                               aForDocumentNavigation);
       if (contentToFocus) {
         NS_ADDREF(*aResultContent = contentToFocus);
         return NS_OK;
       }
     }
 
     // If we reach here, it means no next tabbable content in shadow DOM.
     // We need to continue searching in light DOM, starting at the shadow host
@@ -3604,18 +3633,20 @@ nsFocusManager::GetNextTabbableContent(n
       // and later in "For each element ELEMENT in NAVIGATION-ORDER: "
       // hosts and slots are handled before other elements.
       if (currentContent && nsDocument::IsShadowDOMEnabled(currentContent) &&
           IsHostOrSlot(currentContent)) {
         int32_t tabIndex = HostOrSlotTabIndexValue(currentContent);
         if (tabIndex >= 0 &&
             (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
           nsIContent* contentToFocus =
-            GetNextTabbableContentInScope(currentContent, currentContent, aForward,
+            GetNextTabbableContentInScope(currentContent, currentContent,
+                                          aOriginalStartContent, aForward,
                                           aForward ? 1 : 0, aIgnoreTabIndex,
+                                          aForDocumentNavigation,
                                           true /* aSkipOwner */);
           if (contentToFocus) {
             NS_ADDREF(*aResultContent = contentToFocus);
             return NS_OK;
           }
         }
       }
 
@@ -3662,64 +3693,31 @@ nsFocusManager::GetNextTabbableContent(n
           TabParent* remote = TabParent::GetFrom(currentContent);
           if (remote) {
             remote->NavigateByKey(aForward, aForDocumentNavigation);
             return NS_SUCCESS_DOM_NO_OPERATION;
           }
 
           // Next, for document navigation, check if this a non-remote child document.
           bool checkSubDocument = true;
-          if (aForDocumentNavigation) {
-            Element* docRoot = GetRootForChildDocument(currentContent);
-            if (docRoot) {
-              // If GetRootForChildDocument returned something then call
-              // FocusFirst to find the root or first element to focus within
-              // the child document. If this is a frameset though, skip this and
-              // fall through to the checkSubDocument block below to iterate into
-              // the frameset's frames and locate the first focusable frame.
-              if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
-                return FocusFirst(docRoot, aResultContent);
-              }
-            } else {
-              // Set checkSubDocument to false, as this was neither a frame
-              // type element or a child document that was focusable.
-              checkSubDocument = false;
-            }
+          if (aForDocumentNavigation &&
+              TryDocumentNavigation(currentContent, &checkSubDocument,
+                                    aResultContent)) {
+            return NS_OK;
           }
 
           if (checkSubDocument) {
             // found a node with a matching tab index. Check if it is a child
             // frame. If so, navigate into the child frame instead.
-            nsIDocument* doc = currentContent->GetComposedDoc();
-            NS_ASSERTION(doc, "content not in document");
-            nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
-            if (subdoc && !subdoc->EventHandlingSuppressed()) {
-              if (aForward) {
-                // when tabbing forward into a frame, return the root
-                // frame so that the canvas becomes focused.
-                nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
-                if (subframe) {
-                  *aResultContent = GetRootForFocus(subframe, subdoc, false, true);
-                  if (*aResultContent) {
-                    NS_ADDREF(*aResultContent);
-                    return NS_OK;
-                  }
-                }
-              }
-              Element* rootElement = subdoc->GetRootElement();
-              nsIPresShell* subShell = subdoc->GetShell();
-              if (rootElement && subShell) {
-                rv = GetNextTabbableContent(subShell, rootElement,
-                                            aOriginalStartContent, rootElement,
-                                            aForward, (aForward ? 1 : 0),
-                                            false, aForDocumentNavigation, aResultContent);
-                NS_ENSURE_SUCCESS(rv, rv);
-                if (*aResultContent)
-                  return NS_OK;
-              }
+            if (TryToMoveFocusToSubDocument(currentContent,
+                                            aOriginalStartContent,
+                                            aForward, aForDocumentNavigation,
+                                            aResultContent)) {
+              MOZ_ASSERT(*aResultContent);
+              return NS_OK;
             }
             // otherwise, use this as the next content node to tab to, unless
             // this was the element we started on. This would happen for
             // instance on an element with child frames, where frame navigation
             // could return the original element again. In that case, just skip
             // it. Also, if the next content node is the root content, then
             // return it. This latter case would happen only if someone made a
             // popup focusable.
@@ -3733,18 +3731,20 @@ nsFocusManager::GetNextTabbableContent(n
 
               if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
                 // If currentContent is a shadow host in backward
                 // navigation, search in scope owned by currentContent
                 if (!aForward && currentContent->GetShadowRoot()) {
                   nsIContent* contentToFocus =
                     GetNextTabbableContentInScope(currentContent,
                                                   currentContent,
+                                                  aOriginalStartContent,
                                                   aForward, aForward ? 1 : 0,
                                                   aIgnoreTabIndex,
+                                                  aForDocumentNavigation,
                                                   true /* aSkipOwner */);
                   if (contentToFocus) {
                     NS_ADDREF(*aResultContent = contentToFocus);
                     return NS_OK;
                   }
                 }
               }
 
@@ -3798,16 +3798,83 @@ nsFocusManager::GetNextTabbableContent(n
     // continue looking for next highest priority tabindex
     aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
     startContent = iterStartContent = aRootContent;
   }
 
   return NS_OK;
 }
 
+bool
+nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
+                                      bool* aCheckSubDocument,
+                                      nsIContent** aResultContent)
+{
+  *aCheckSubDocument = true;
+  Element* docRoot = GetRootForChildDocument(aCurrentContent);
+  if (docRoot) {
+    // If GetRootForChildDocument returned something then call
+    // FocusFirst to find the root or first element to focus within
+    // the child document. If this is a frameset though, skip this and
+    // fall through to normal tab navigation to iterate into
+    // the frameset's frames and locate the first focusable frame.
+    if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
+      *aCheckSubDocument = false;
+      Unused << FocusFirst(docRoot, aResultContent);
+      return *aResultContent != nullptr;
+    }
+  } else {
+    // Set aCheckSubDocument to false, as this was neither a frame
+    // type element or a child document that was focusable.
+    *aCheckSubDocument = false;
+  }
+
+  return false;
+}
+
+bool
+nsFocusManager::TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
+                                            nsIContent* aOriginalStartContent,
+                                            bool aForward,
+                                            bool aForDocumentNavigation,
+                                            nsIContent** aResultContent)
+{
+  nsIDocument* doc = aCurrentContent->GetComposedDoc();
+  NS_ASSERTION(doc, "content not in document");
+  nsIDocument* subdoc = doc->GetSubDocumentFor(aCurrentContent);
+  if (subdoc && !subdoc->EventHandlingSuppressed()) {
+    if (aForward) {
+      // when tabbing forward into a frame, return the root
+      // frame so that the canvas becomes focused.
+      nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
+      if (subframe) {
+        *aResultContent = GetRootForFocus(subframe, subdoc, false, true);
+        if (*aResultContent) {
+          NS_ADDREF(*aResultContent);
+          return true;
+        }
+      }
+    }
+    Element* rootElement = subdoc->GetRootElement();
+    nsIPresShell* subShell = subdoc->GetShell();
+    if (rootElement && subShell) {
+      nsresult rv = GetNextTabbableContent(subShell, rootElement,
+                                           aOriginalStartContent, rootElement,
+                                           aForward, (aForward ? 1 : 0),
+                                           false, aForDocumentNavigation,
+                                           aResultContent);
+      NS_ENSURE_SUCCESS(rv, false);
+      if (*aResultContent) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 nsIContent*
 nsFocusManager::GetNextTabbableMapArea(bool aForward,
                                        int32_t aCurrentTabIndex,
                                        Element* aImageContent,
                                        nsIContent* aStartContent)
 {
   nsAutoString useMap;
   aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -457,67 +457,83 @@ protected:
   /**
    * Retrieve the next tabbable element in scope owned by aOwner, using
    * focusability and tabindex to determine the tab order.
    *
    * aOwner is the owner of scope to search in.
    *
    * aStartContent is the starting point for this call of this method.
    *
+   * aOriginalStartContent is the initial starting point for sequential
+   * navigation.
+   *
    * aForward should be true for forward navigation or false for backward
    * navigation.
    *
    * aCurrentTabIndex is the current tabindex.
    *
    * aIgnoreTabIndex to ignore the current tabindex and find the element
    * irrespective or the tab index.
    *
+   * aForDocumentNavigation informs whether we're navigating only through
+   * documents.
+   *
    * aSkipOwner to skip owner while searching. The flag is set when caller is
    * |GetNextTabbableContent| in order to let caller handle owner.
    *
    * NOTE:
    *   Consider the method searches downwards in flattened subtree
    *   rooted at aOwner.
    */
   nsIContent* GetNextTabbableContentInScope(nsIContent* aOwner,
                                             nsIContent* aStartContent,
+                                            nsIContent* aOriginalStartContent,
                                             bool aForward,
                                             int32_t aCurrentTabIndex,
                                             bool aIgnoreTabIndex,
+                                            bool aForDocumentNavigation,
                                             bool aSkipOwner);
 
   /**
    * Retrieve the next tabbable element in scope including aStartContent
    * and the scope's ancestor scopes, using focusability and tabindex to
    * determine the tab order.
    *
    * aStartContent an in/out paremeter. It as input is the starting point
    * for this call of this method; as output it is the shadow host in
    * light DOM if the next tabbable element is not found in shadow DOM,
    * in order to continue searching in light DOM.
    *
+   * aOriginalStartContent is the initial starting point for sequential
+   * navigation.
+   *
    * aForward should be true for forward navigation or false for backward
    * navigation.
    *
    * aCurrentTabIndex returns tab index of shadow host in light DOM if the
    * next tabbable element is not found in shadow DOM, in order to continue
    * searching in light DOM.
    *
    * aIgnoreTabIndex to ignore the current tabindex and find the element
    * irrespective or the tab index.
    *
+   * aForDocumentNavigation informs whether we're navigating only through
+   * documents.
+   *
    * NOTE:
    *   Consider the method searches upwards in all shadow host- or slot-rooted
    *   flattened subtrees that contains aStartContent as non-root, except
    *   the flattened subtree rooted at shadow host in light DOM.
    */
   nsIContent* GetNextTabbableContentInAncestorScopes(nsIContent** aStartContent,
+                                                     nsIContent* aOriginalStartContent,
                                                      bool aForward,
                                                      int32_t* aCurrentTabIndex,
-                                                     bool aIgnoreTabIndex);
+                                                     bool aIgnoreTabIndex,
+                                                     bool aForDocumentNavigation);
 
   /**
    * Retrieve the next tabbable element within a document, using focusability
    * and tabindex to determine the tab order. The element is returned in
    * aResultContent.
    *
    * aRootContent is the root node -- nodes above this will not be examined.
    * Typically this will be the root node of a document, but could also be
@@ -629,16 +645,26 @@ private:
   // wrong..
   static void NotifyFocusStateChange(nsIContent* aContent,
                                      nsIContent* aContentToFocus,
                                      bool aWindowShouldShowFocusRing,
                                      bool aGettingFocus);
 
   void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
 
+  bool TryDocumentNavigation(nsIContent* aCurrentContent,
+                             bool* aCheckSubDocument,
+                             nsIContent** aResultContent);
+
+  bool TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
+                                   nsIContent* aOriginalStartContent,
+                                   bool aForward,
+                                   bool aForDocumentNavigation,
+                                   nsIContent** aResultContent);
+
   // the currently active and front-most top-most window
   nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;
 
   // the child or top-level window that is currently focused. This window will
   // either be the same window as mActiveWindow or a descendant of it.
   // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow!
   nsCOMPtr<nsPIDOMWindowOuter> mFocusedWindow;
 
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -5199,30 +5199,21 @@ nsGlobalWindowInner::FireOfflineStatusEv
   mWasOffline = !mWasOffline;
 
   nsAutoString name;
   if (mWasOffline) {
     name.AssignLiteral("offline");
   } else {
     name.AssignLiteral("online");
   }
-  // The event is fired at the body element, or if there is no body element,
-  // at the document.
-  nsCOMPtr<EventTarget> eventTarget = mDoc.get();
-  if (mDoc->IsHTMLOrXHTML()) {
-    if (Element* body = mDoc->GetBody()) {
-      eventTarget = body;
-    }
-  } else {
-    Element* documentElement = mDoc->GetDocumentElement();
-    if (documentElement) {
-      eventTarget = documentElement;
-    }
-  }
-  nsContentUtils::DispatchTrustedEvent(mDoc, eventTarget, name, true, false);
+  nsContentUtils::DispatchTrustedEvent(mDoc,
+                                       static_cast<EventTarget*>(this),
+                                       name,
+                                       false,
+                                       false);
 }
 
 class NotifyIdleObserverRunnable : public Runnable
 {
 public:
   NotifyIdleObserverRunnable(nsIIdleObserver* aIdleObserver,
                              uint32_t aTimeInS,
                              bool aCallOnidle,
--- a/dom/base/test/browser_bug1303838.js
+++ b/dom/base/test/browser_bug1303838.js
@@ -161,14 +161,14 @@ function waitForLocationChange(browser, 
     locationChangeListener = {
       onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
         info("LocationChange: " + aLocation.spec);
         if (++seen == locationChangeNum) {
           browser.removeProgressListener(this);
           resolve();
         }
       },
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                             Ci.nsISupportsWeakReference])
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                              Ci.nsISupportsWeakReference])
     };
     browser.addProgressListener(locationChangeListener);
   });
 }
--- a/dom/base/test/browser_promiseDocumentFlushed.js
+++ b/dom/base/test/browser_promiseDocumentFlushed.js
@@ -116,18 +116,18 @@ add_task(async function test_can_get_res
   assertFlushesRequired();
 
   let rect = await window.promiseDocumentFlushed(() => {
     let observer = {
       reflow() {
         Assert.ok(false, "A reflow should not have occurred.");
       },
       reflowInterruptible() {},
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
-                                             Ci.nsISupportsWeakReference])
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
+                                              Ci.nsISupportsWeakReference])
     };
 
     let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShell);
     docShell.addWeakReflowObserver(observer);
 
     let toolboxRect = gNavToolbox.getBoundingClientRect();
--- a/dom/base/test/chrome/file_bug549682.xul
+++ b/dom/base/test/chrome/file_bug549682.xul
@@ -87,33 +87,33 @@ https://bugzilla.mozilla.org/show_bug.cg
   function localL(m) {
     is(m.name, "lasync", "Wrong message!");
     is(m.json.data, 2345, "Wrong data!");
     didRunLocal = true;
   }
 
   var weakMessageReceived = false;
   var weakListener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference]),
 
     receiveMessage: function(msg) {
       if (weakMessageReceived) {
         ok(false, 'Weak listener fired twice.');
         return;
       }
 
       ok(true, 'Weak listener fired once.');
       weakMessageReceived = true;
       document.getElementById('ifr').messageManager
               .removeWeakMessageListener('weak', weakListener);
     }
   };
 
   var weakListener2 = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference]),
 
     receiveMessage: function(msg) {
       ok(false, 'Should not have received a message.');
     }
   };
 
   function weakDoneListener() {
     ok(weakMessageReceived, 'Got "weak" message.');
--- a/dom/base/test/chrome/test_bug682305.html
+++ b/dom/base/test/chrome/test_bug682305.html
@@ -88,17 +88,17 @@ CustomChannel.prototype = {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
   suspend: function() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
   resume: function() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
 };
 
 
 function CustomProtocol() {}
 CustomProtocol.prototype = {
   get scheme() {
     return PROTOCOL_SCHEME;
   },
@@ -120,19 +120,17 @@ CustomProtocol.prototype = {
              .finalize();
   },
   newChannel2: function newChannel2(URI, loadInfo) {
     return new CustomChannel(URI, loadInfo);
   },
   newChannel: function newChannel(URI) {
     return newChannel2(URI);
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIProtocolHandler])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIProtocolHandler])
 };
 
 var gFactory = {
   register: function() {
     var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 
     var classID = Components.ID("{ed064287-1e76-49ba-a28d-dc74394a8334}");
     var description = PROTOCOL_SCHEME + ": protocol";
--- a/dom/base/test/chrome/test_bug800386.xul
+++ b/dom/base/test/chrome/test_bug800386.xul
@@ -41,18 +41,17 @@ https://bugzilla.mozilla.org/show_bug.cg
           return xhr2.getInterface(aIID);
         } catch (e) {
           forwardFailed = true;
         }
       }
       throw Cr.NS_ERROR_NO_INTERFACE;
     },
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                           Ci.nsIInterfaceRequestor])
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIInterfaceRequestor])
   };
 
   // HTTP URI so that we get progress callbacks
   xhr.open("GET", "http://mochi.test:8888/", false);
   xhr.channel.notificationCallbacks = requestor;
   xhr.onreadystatechange = function() {
     if (xhr.readyState == 4) {
       ok(triedForwarding,
--- a/dom/base/test/file_bug1453693.html
+++ b/dom/base/test/file_bug1453693.html
@@ -32,16 +32,21 @@
         sr.appendChild(shadowInput);
 
         var shadowDate = document.createElement("input");
         shadowDate.type = "date";
         shadowDate.onfocus = focusLogger;
         shadowDate.tabIndex = 1;
         sr.appendChild(shadowDate);
 
+        var shadowIframe = document.createElement("iframe");
+        shadowIframe.tabIndex = 1;
+        sr.appendChild(shadowIframe);
+        shadowIframe.contentDocument.body.innerHTML = "<input>";
+
         var input = document.createElement("input");
         input.onfocus = focusLogger;
         input.tabIndex = 1;
         document.body.appendChild(input);
 
         var input2 = document.createElement("input");
         input2.onfocus = focusLogger;
         document.body.appendChild(input2);
@@ -56,24 +61,40 @@
         opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
         synthesizeKey("KEY_Tab");
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
         synthesizeKey("KEY_Tab");
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
         synthesizeKey("KEY_Tab");
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
         synthesizeKey("KEY_Tab");
+        opener.is(shadowIframe.contentDocument.activeElement,
+                  shadowIframe.contentDocument.documentElement,
+                  "Should have focused document element in shadow iframe. (3)");
+        synthesizeKey("KEY_Tab");
+        opener.is(shadowIframe.contentDocument.activeElement,
+                  shadowIframe.contentDocument.body.firstChild,
+                  "Should have focused input element in shadow iframe. (3)");
+        synthesizeKey("KEY_Tab");
         opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)");
         synthesizeKey("KEY_Tab");
         opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)");
 
         // Backwards
         synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)");
         synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(shadowIframe.contentDocument.activeElement,
+                  shadowIframe.contentDocument.body.firstChild,
+                  "Should have focused input element in shadow iframe. (4)");
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(shadowIframe.contentDocument.activeElement,
+                  shadowIframe.contentDocument.documentElement,
+                  "Should have focused document element in shadow iframe. (4)");
+        synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
         synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
         synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
         synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)");
         synthesizeKey("KEY_Tab", {shiftKey: true});
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3870,17 +3870,18 @@ HTMLConstructor(JSContext* aCx, unsigned
     RefPtr<NodeInfo> nodeInfo =
       doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
                                           nullptr,
                                           ns,
                                           nsINode::ELEMENT_NODE);
     MOZ_ASSERT(nodeInfo);
 
     if (ns == kNameSpaceID_XUL) {
-      element = new nsXULElement(nodeInfo.forget());
+      element = nsXULElement::Construct(nodeInfo.forget());
+
     } else {
       if (tag == eHTMLTag_userdefined) {
         // Autonomous custom element.
         element = NS_NewHTMLElement(nodeInfo.forget());
       } else {
         // Customized built-in element.
         element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
       }
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -102,20 +102,16 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::AudioContext',
 },
 
 'BatteryManager': {
     'nativeType': 'mozilla::dom::battery::BatteryManager',
     'headerFile': 'BatteryManager.h'
 },
 
-'BoxObject': {
-    'resultNotAddRefed': ['element'],
-},
-
 'Cache': {
     'implicitJSContext': [ 'add', 'addAll', 'match', 'matchAll', 'put',
                            'delete', 'keys' ],
     'nativeType': 'mozilla::dom::cache::Cache',
 },
 
 'CacheStorage': {
     'implicitJSContext': [ 'match' ],
@@ -686,20 +682,16 @@ DOMInterfaces = {
 'PluginArray': {
     'nativeType': 'nsPluginArray',
 },
 
 'PluginTag': {
     'nativeType': 'nsIPluginTag',
 },
 
-'PopupBoxObject': {
-    'resultNotAddRefed': ['triggerNode', 'anchorNode'],
-},
-
 'Position': {
     'headerFile': 'nsGeoPosition.h'
 },
 
 'PositionError': {
     'headerFile': 'nsGeolocation.h'
 },
 
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -8,19 +8,17 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function TestInterfaceJS(anyArg, objectArg) {}
 
 TestInterfaceJS.prototype = {
   classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
   contractID: "@mozilla.org/dom/test-interface-js;1",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.mozITestInterfaceJS]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer, Ci.mozITestInterfaceJS]),
 
   init: function(win) { this._win = win; },
 
   __init: function (anyArg, objectArg, dictionaryArg) {
     this._anyAttr = undefined;
     this._objectAttr = null;
     this._anyArg = anyArg;
     this._objectArg = objectArg;
--- a/dom/bindings/test/TestInterfaceJSMaplike.js
+++ b/dom/bindings/test/TestInterfaceJSMaplike.js
@@ -8,18 +8,17 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function TestInterfaceJSMaplike() {}
 
 TestInterfaceJSMaplike.prototype = {
   classID: Components.ID("{4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}"),
   contractID: "@mozilla.org/dom/test-interface-js-maplike;1",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
 
   init: function(win) { this._win = win; },
 
   __init: function () {},
 
   setInternal: function(aKey, aValue) {
     return this.__DOM_IMPL__.__set(aKey, aValue);
   },
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -118,18 +118,18 @@ function BrowserElementChild() {
 
   this._isContentWindowCreated = false;
 
   this._init();
 };
 
 BrowserElementChild.prototype = {
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   _init: function() {
     debug("Starting up.");
 
     BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
 
     docShell.QueryInterface(Ci.nsIWebProgress)
             .addProgressListener(this._progressListener,
@@ -1375,18 +1375,18 @@ BrowserElementChild.prototype = {
     }
     this._finder.removeSelection();
     sendAsyncMsg("findchange", {active: false});
   },
 
   // The docShell keeps a weak reference to the progress listener, so we need
   // to keep a strong ref to it ourselves.
   _progressListener: {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                            Ci.nsISupportsWeakReference]),
     _seenLoadStart: false,
 
     onLocationChange: function(webProgress, request, location, flags) {
       // We get progress events from subshells here, which is kind of weird.
       if (webProgress != docShell) {
         return;
       }
 
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -69,19 +69,19 @@ function BrowserElementParent() {
   Services.obs.addObserver(this, 'back-docommand', /* ownsWeak = */ true);
 }
 
 BrowserElementParent.prototype = {
 
   classDescription: "BrowserElementAPI implementation",
   classID: Components.ID("{9f171ac4-0939-4ef8-b360-3408aedc3060}"),
   contractID: "@mozilla.org/dom/browser-element-api;1",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserElementAPI,
-                                         Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIBrowserElementAPI,
+                                          Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   setFrameLoader: function(frameLoader) {
     debug("Setting frameLoader");
     this._frameLoader = frameLoader;
     this._frameElement = frameLoader.ownerElement;
     if (!this._frameElement) {
       debug("No frame element?");
       return;
@@ -723,18 +723,18 @@ BrowserElementParent.prototype = {
           this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
         }
       },
       onDataAvailable: function(aRequest, aContext, aInputStream,
                                 aOffset, aCount) {
         this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
                                          aOffset, aCount);
       },
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener,
-                                             Ci.nsIRequestObserver])
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener,
+                                              Ci.nsIRequestObserver])
     };
 
     let referrer = Services.io.newURI(_options.referrer);
     let principal =
       Services.scriptSecurityManager.createCodebasePrincipal(
         referrer, this._frameLoader.loadContext.originAttributes);
 
     let channel = NetUtil.newChannel({
--- a/dom/browser-element/BrowserElementPromptService.jsm
+++ b/dom/browser-element/BrowserElementPromptService.jsm
@@ -20,17 +20,17 @@ function debug(msg) {
 }
 
 function BrowserElementPrompt(win, browserElementChild) {
   this._win = win;
   this._browserElementChild = browserElementChild;
 }
 
 BrowserElementPrompt.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
 
   alert: function(title, text) {
     this._browserElementChild.showModalPrompt(
       this._win, {promptType: "alert", title: title, message: text, returnValue: undefined});
   },
 
   alertCheck: function(title, text, checkMsg, checkState) {
     // Treat this like a normal alert() call, ignoring the checkState.  The
@@ -207,17 +207,17 @@ BrowserElementPrompt.prototype = {
   },
 };
 
 
 function BrowserElementAuthPrompt() {
 }
 
 BrowserElementAuthPrompt.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAuthPrompt2]),
 
   promptAuth: function promptAuth(channel, level, authInfo) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
   asyncPromptAuth: function asyncPromptAuth(channel, callback, context, level, authInfo) {
     debug("asyncPromptAuth");
 
@@ -237,17 +237,17 @@ BrowserElementAuthPrompt.prototype = {
       BrowserElementPromptService.getBrowserElementParentForFrame(frame);
 
     if (!browserElementParent) {
       debug("Failed to load browser element parent.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     let consumer = {
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+      QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
       callback: callback,
       context: context,
       cancel: function() {
         this.callback.onAuthCancelled(this.context, false);
         this.callback = null;
         this.context = null;
       }
     };
@@ -436,17 +436,17 @@ BrowserElementAuthPrompt.prototype = {
 
 
 function AuthPromptWrapper(oldImpl, browserElementImpl) {
   this._oldImpl = oldImpl;
   this._browserElementImpl = browserElementImpl;
 }
 
 AuthPromptWrapper.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAuthPrompt2]),
   promptAuth: function(channel, level, authInfo) {
     if (this._canGetParentElement(channel)) {
       return this._browserElementImpl.promptAuth(channel, level, authInfo);
     } else {
       return this._oldImpl.promptAuth(channel, level, authInfo);
     }
   },
 
@@ -478,17 +478,17 @@ AuthPromptWrapper.prototype = {
 };
 
 function BrowserElementPromptFactory(toWrap) {
   this._wrapped = toWrap;
 }
 
 BrowserElementPromptFactory.prototype = {
   classID: Components.ID("{24f3d0cf-e417-4b85-9017-c9ecf8bb1299}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptFactory]),
 
   _mayUseNativePrompt: function() {
     try {
       return Services.prefs.getBoolPref("browser.prompt.allowNative");
     } catch (e) {
       // This properity is default to true.
       return true;
     }
@@ -558,18 +558,18 @@ BrowserElementPromptFactory.prototype = 
 
     debug("Returning wrapped getPrompt for " + win);
     return new BrowserElementPrompt(win, browserElementChild)
                                    .QueryInterface(iid);
   }
 };
 
 var BrowserElementPromptService = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
 
   _initialized: false,
 
   _init: function() {
     if (this._initialized) {
       return;
     }
 
--- a/dom/console/ConsoleAPIStorage.js
+++ b/dom/console/ConsoleAPIStorage.js
@@ -39,18 +39,18 @@ const CONSOLEAPISTORAGE_CID = Components
  *    ConsoleAPIStorage.clearEvents(innerWindowID);
  */
 function ConsoleAPIStorageService() {
   this.init();
 }
 
 ConsoleAPIStorageService.prototype = {
   classID : CONSOLEAPISTORAGE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleAPIStorage,
-                                         Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleAPIStorage,
+                                          Ci.nsIObserver]),
   classInfo: XPCOMUtils.generateCI({
     classID: CONSOLEAPISTORAGE_CID,
     contractID: '@mozilla.org/consoleAPI-storage;1',
     interfaces: [Ci.nsIConsoleAPIStorage, Ci.nsIObserver],
     flags: Ci.nsIClassInfo.SINGLETON
   }),
 
   observe: function CS_observe(aSubject, aTopic, aData)
--- a/dom/events/test/test_bug336682.js
+++ b/dom/events/test/test_bug336682.js
@@ -11,33 +11,16 @@ var gState = 0;
  */
 var MAX_STATE;
 
 function trace(text) {
   var t = text.replace(/&/g, "&" + "amp;").replace(/</g, "&" + "lt;") + "<br>";
   //document.getElementById("display").innerHTML += t;
 }
 
-// window.ononline and window.onclick shouldn't work
-// Right now, <body ononline=...> sets window.ononline (bug 380618)
-// When these start passing, be sure to uncomment the code inside if(0) below.
-todo(typeof window.ononline == "undefined",
-     "window.ononline should be undefined at this point");
-todo(typeof window.onoffline == "undefined",
-     "window.onoffline should be undefined at this point");
-
-if (0) {
-  window.ononline = function() {
-    ok(false, "window.ononline shouldn't be called");
-  }
-  window.onoffline = function() {
-    ok(false, "window.onclick shouldn't be called");
-  }
-}
-
 /**
  * Returns a handler function for an online/offline event. The returned handler
  * ensures the passed event object has expected properties and that the handler
  * is called at the right moment (according to the gState variable).
  * @param nameTemplate The string identifying the hanlder. '%1' in that
  *                     string will be replaced with the event name.
  * @param eventName 'online' or 'offline'
  * @param expectedStates an array listing the possible values of gState at the
@@ -50,21 +33,19 @@ function makeHandler(nameTemplate, event
     var name = nameTemplate.replace(/%1/, eventName);
     ++gState;
     trace(name + ": gState=" + gState);
     ok(expectedStates.includes(gState),
        "handlers called in the right order: " + name + " is called, " + 
        "gState=" + gState + ", expectedStates=" + expectedStates);
     ok(e.constructor == Event, "event should be an Event");
     ok(e.type == eventName, "event type should be " + eventName);
-    ok(e.bubbles, "event should bubble");
+    ok(!e.bubbles, "event should not bubble");
     ok(!e.cancelable, "event should not be cancelable");
-    ok(e.target == (document instanceof HTMLDocument
-                    ? document.body : document.documentElement),
-       "the event target should be the body element");
+    ok(e.target == window, "target should be the window");
   }
 }
 
 function doTest() {
   var iosvc = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                            .getService(SpecialPowers.Ci.nsIIOService);
   iosvc.manageOfflineStatus = false;
   iosvc.offline = false;
--- a/dom/events/test/test_bug336682_1.html
+++ b/dom/events/test/test_bug336682_1.html
@@ -23,41 +23,31 @@ http://creativecommons.org/licenses/publ
 <pre id="test">
 </pre>
 <script type="text/javascript" src="test_bug336682.js"></script>
 
 <script class="testbody" type="text/javascript">
 
 function makeBodyHandler(eventName) {
   return function (aThis, aEvent) {
-    var handler = makeHandler("<body on%1='...'>", eventName, [3,4]);
+    var handler = makeHandler("<body on%1='...'>", eventName, [1]);
     handler(aEvent);
   }
 }
 addLoadEvent(function() {
   /** @see test_bug336682.js */
-  MAX_STATE = 4;
+  MAX_STATE = 2;
 
   for (var event of ["online", "offline"]) {
-    document.body.addEventListener(
-      event,
-      makeHandler("document.body.addEventListener('%1', ..., false)",
-                  event, [1]));
-
-    document.addEventListener(
-      event,
-      makeHandler("document.addEventListener('%1', ..., false)",
-                  event, [2]));
-
     window["bodyOn" + event] = makeBodyHandler(event);
 
     window.addEventListener(
       event,
       makeHandler("window.addEventListener('%1', ..., false)",
-                  event, [3,4]));
+                  event, [2]));
   }
 
   doTest();
   SimpleTest.finish();
 });
 
 SimpleTest.waitForExplicitFinish();
 </script>
--- a/dom/events/test/test_bug336682_2.xul
+++ b/dom/events/test/test_bug336682_2.xul
@@ -24,44 +24,32 @@ Mozilla Bug 336682 (online/offline event
 </div>
 </body>
 
 <script type="text/javascript" src="test_bug336682.js"/>
 <script class="testbody" type="text/javascript">
 <![CDATA[
 addLoadEvent(function() {
   /** @see test_bug336682.js */
-  MAX_STATE = 4;
+  MAX_STATE = 2;
 
   function makeWindowHandler(eventName) {
     return function (aThis, aEvent) {
-      var handler = makeHandler("<body on%1='...'>", eventName, [3,4]);
+      var handler = makeHandler("<body on%1='...'>", eventName, [1]);
       handler(aEvent);
     }
   }
 
   for (var event of ["online", "offline"]) {
-    document.documentElement.addEventListener(
-      event,
-      makeHandler("document.body.addEventListener('%1', ..., false)",
-                  event, [1]),
-      false);
-
-    document.addEventListener(
-      event,
-      makeHandler("document.addEventListener('%1', ..., false)",
-                  event, [2]),
-      false);
-
     window["windowOn" + event] = makeWindowHandler(event);
 
     window.addEventListener(
       event,
       makeHandler("window.addEventListener('%1', ..., false)",
-                  event, [3,4]),
+                  event, [2]),
       false);
   }
 
   doTest();
   SimpleTest.finish();
 });
 
 SimpleTest.waitForExplicitFinish();
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -779,17 +779,18 @@ HTMLFormElement::SubmitSubmission(HTMLFo
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
                                       VoidString(),
                                       postDataStream, postDataStreamLength,
                                       nullptr, false,
                                       getter_AddRefs(docShell),
-                                      getter_AddRefs(mSubmittingRequest));
+                                      getter_AddRefs(mSubmittingRequest),
+                                      EventStateManager::IsHandlingUserInput());
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
   // or request; for example, if it's to a named anchor within the same page
   // the submit will not really do anything.
   if (docShell) {
     // If the channel is pending, we have to listen for web progress.
--- a/dom/html/htmlMenuBuilder.js
+++ b/dom/html/htmlMenuBuilder.js
@@ -25,17 +25,17 @@ function HTMLMenuBuilder() {
 // other UI from this object. The default UI is done in PageMenu.jsm.
 //
 // When a multi-process browser is used, the first step is performed by the child
 // process and the second step is performed by the parent process.
 
 HTMLMenuBuilder.prototype =
 {
   classID:        Components.ID("{51c65f5d-0de5-4edc-9058-60e50cef77f8}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMenuBuilder]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIMenuBuilder]),
 
   currentNode: null,
   root: null,
   items: {},
   nestedStack: [],
 
   toJSONString: function() {
     return JSON.stringify(this.root);
--- a/dom/html/test/head.js
+++ b/dom/html/test/head.js
@@ -33,18 +33,18 @@ function waitForDocLoadComplete(aBrowser
         if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
           aBrowser.removeProgressListener(this);
           waitForDocLoadComplete.listeners.delete(this);
           let chan = req.QueryInterface(Ci.nsIChannel);
           info("Browser loaded " + chan.originalURI.spec);
           resolve();
         }
       },
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                             Ci.nsISupportsWeakReference])
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
+                                              Ci.nsISupportsWeakReference])
     };
     aBrowser.addProgressListener(listener);
     waitForDocLoadComplete.listeners.add(listener);
     info("Waiting for browser load");
   });
 }
 // Keep a set of progress listeners for waitForDocLoadComplete() to make sure
 // they're not GC'ed before we saw the page load.
--- a/dom/indexedDB/test/unit/GlobalObjectsComponent.js
+++ b/dom/indexedDB/test/unit/GlobalObjectsComponent.js
@@ -9,17 +9,17 @@ Cu.importGlobalProperties(["indexedDB"])
 function GlobalObjectsComponent() {
   this.wrappedJSObject = this;
 }
 
 GlobalObjectsComponent.prototype =
 {
   classID: Components.ID("{949ebf50-e0da-44b9-8335-cbfd4febfdcc}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([]),
 
   runTest() {
     const name = "Splendid Test";
 
     let ok = this.ok;
     let finishTest = this.finishTest;
 
     let keyRange = IDBKeyRange.only(42);
--- a/dom/media/IdpSandbox.jsm
+++ b/dom/media/IdpSandbox.jsm
@@ -18,17 +18,17 @@ RedirectHttpsOnly.prototype = {
     } else {
       callback.onRedirectVerifyCallback(Cr.NS_OK);
     }
   },
 
   getInterface(iid) {
     return this.QueryInterface(iid);
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIChannelEventSink])
 };
 
 /** This class loads a resource into a single string. ResourceLoader.load() is
  * the entry point. */
 function ResourceLoader(res, rej) {
   this.resolve = res;
   this.reject = rej;
   this.data = "";
@@ -72,17 +72,17 @@ ResourceLoader.prototype = {
     } else {
       this.reject(new Error("Load failed: " + status));
     }
   },
 
   getInterface(iid) {
     return this.QueryInterface(iid);
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener])
 };
 
 /**
  * A simple implementation of the WorkerLocation interface.
  */
 function createLocationFromURI(uri) {
   return {
     href: uri.spec,
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -198,18 +198,18 @@ class GlobalPCList {
     }
   }
 
   _registerPeerConnectionLifecycleCallback(winID, cb) {
     this._lifecycleobservers[winID] = cb;
   }
 }
 setupPrototype(GlobalPCList, {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
   classID: PC_MANAGER_CID,
   _xpcom_factory: {
     createInstance(outer, iid) {
       if (outer) {
         throw Cr.NS_ERROR_NO_AGGREGATION;
       }
       return _globalPCList.QueryInterface(iid);
     }
@@ -225,18 +225,17 @@ class RTCIceCandidate {
 
   __init(dict) {
     Object.assign(this, dict);
   }
 }
 setupPrototype(RTCIceCandidate, {
   classID: PC_ICE_CID,
   contractID: PC_ICE_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer])
 });
 
 class RTCSessionDescription {
   init(win) {
     this._win = win;
     this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
   }
@@ -270,18 +269,17 @@ class RTCSessionDescription {
     let err = this._win.Error();
     logMsg(msg, err.fileName, err.lineNumber, Ci.nsIScriptError.warningFlag,
            this._winID);
   }
 }
 setupPrototype(RTCSessionDescription, {
   classID: PC_SESSION_CID,
   contractID: PC_SESSION_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer])
 });
 
 class RTCStatsReport {
   constructor(win, dict) {
     this._win = win;
     this._pcid = dict.pcid;
     this._report = convertToRTCStatsReport(dict);
   }
@@ -325,17 +323,17 @@ class RTCStatsReport {
 
   __onget(key, value) {
     /* Do whatever here */
   }
 }
 setupPrototype(RTCStatsReport, {
   classID: PC_STATS_CID,
   contractID: PC_STATS_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
+  QueryInterface: ChromeUtils.generateQI([]),
   _specToLegacyFieldMapping: {
         "inbound-rtp": "inboundrtp",
         "outbound-rtp": "outboundrtp",
         "candidate-pair": "candidatepair",
         "local-candidate": "localcandidate",
         "remote-candidate": "remotecandidate"
   }
 });
@@ -1602,18 +1600,17 @@ class RTCPeerConnection {
     this.updateNegotiationNeeded();
 
     return dataChannel;
   }
 }
 setupPrototype(RTCPeerConnection, {
   classID: PC_CID,
   contractID: PC_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
   _actions: {
     offer: Ci.IPeerConnection.kActionOffer,
     answer: Ci.IPeerConnection.kActionAnswer,
     pranswer: Ci.IPeerConnection.kActionPRAnswer,
     rollback: Ci.IPeerConnection.kActionRollback,
   },
 });
 
@@ -1861,35 +1858,33 @@ class PeerConnectionObserver {
 
   syncTransceivers() {
     this._dompc._syncTransceivers();
   }
 }
 setupPrototype(PeerConnectionObserver, {
   classID: PC_OBS_CID,
   contractID: PC_OBS_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer])
 });
 
 class RTCPeerConnectionStatic {
   init(win) {
     this._winID = win.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
   }
 
   registerPeerConnectionLifecycleCallback(cb) {
     _globalPCList._registerPeerConnectionLifecycleCallback(this._winID, cb);
   }
 }
 setupPrototype(RTCPeerConnectionStatic, {
   classID: PC_STATIC_CID,
   contractID: PC_STATIC_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer])
 });
 
 class RTCDTMFSender {
   constructor(sender) {
     this._sender = sender;
   }
 
   get toneBuffer() {
@@ -1907,17 +1902,17 @@ class RTCDTMFSender {
   insertDTMF(tones, duration, interToneGap) {
     this._sender._pc._checkClosed();
     this._sender._transceiver.insertDTMF(tones, duration, interToneGap);
   }
 }
 setupPrototype(RTCDTMFSender, {
   classID: PC_DTMF_SENDER_CID,
   contractID: PC_DTMF_SENDER_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
+  QueryInterface: ChromeUtils.generateQI([])
 });
 
 class RTCRtpSender {
   constructor(pc, transceiverImpl, transceiver, track, kind, streams) {
     let dtmf = null;
     if (kind == "audio") {
       dtmf = pc._win.RTCDTMFSender._create(pc._win, new RTCDTMFSender(this));
     }
@@ -2049,17 +2044,17 @@ class RTCRtpSender {
           "This sender was not created by this PeerConnection",
           "InvalidAccessError");
     }
   }
 }
 setupPrototype(RTCRtpSender, {
   classID: PC_SENDER_CID,
   contractID: PC_SENDER_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
+  QueryInterface: ChromeUtils.generateQI([])
 });
 
 class RTCRtpReceiver {
   constructor(pc, transceiverImpl) {
     // We do not set the track here; that is done when _transceiverImpl is set
     Object.assign(this,
         {
           _pc: pc,
@@ -2219,17 +2214,17 @@ class RTCRtpReceiver {
           { track: this.track });
       trackEvents.push(ev);
     }
   }
 }
 setupPrototype(RTCRtpReceiver, {
   classID: PC_RECEIVER_CID,
   contractID: PC_RECEIVER_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
+  QueryInterface: ChromeUtils.generateQI([])
 });
 
 class RTCRtpTransceiver {
   constructor(pc, transceiverImpl, init, kind, sendTrack) {
     let receiver = pc._win.RTCRtpReceiver._create(
         pc._win, new RTCRtpReceiver(pc, transceiverImpl, kind));
     let streams = (init && init.streams) || [];
     let sender = pc._win.RTCRtpSender._create(
@@ -2382,28 +2377,28 @@ class RTCRtpTransceiver {
     // extra hops through RTCPeerConnection and PeerConnectionImpl
     this._pc._insertDTMF(this._transceiverImpl, tones, duration, interToneGap);
   }
 }
 
 setupPrototype(RTCRtpTransceiver, {
   classID: PC_TRANSCEIVER_CID,
   contractID: PC_TRANSCEIVER_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
+  QueryInterface: ChromeUtils.generateQI([])
 });
 
 class CreateOfferRequest {
   constructor(windowID, innerWindowID, callID, isSecure) {
     Object.assign(this, { windowID, innerWindowID, callID, isSecure });
   }
 }
 setupPrototype(CreateOfferRequest, {
   classID: PC_COREQUEST_CID,
   contractID: PC_COREQUEST_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
+  QueryInterface: ChromeUtils.generateQI([])
 });
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList,
    RTCDTMFSender,
    RTCIceCandidate,
    RTCSessionDescription,
    RTCPeerConnection,
--- a/dom/media/webvtt/WebVTTParserWrapper.js
+++ b/dom/media/webvtt/WebVTTParserWrapper.js
@@ -54,17 +54,17 @@ WebVTTParserWrapper.prototype =
 
   processCues: function(window, cues, overlay, controls)
   {
     WebVTT.processCues(window, cues, overlay, controls);
   },
 
   classDescription: "Wrapper for the JS WebVTT implementation (vtt.js)",
   classID: Components.ID(WEBVTTPARSERWRAPPER_CID),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebVTTParserWrapper]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebVTTParserWrapper]),
   classInfo: XPCOMUtils.generateCI({
     classID:    WEBVTTPARSERWRAPPER_CID,
     contractID: WEBVTTPARSERWRAPPER_CONTRACTID,
     interfaces: [Ci.nsIWebVTTParserWrapper]
   })
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebVTTParserWrapper]);
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -251,13 +251,13 @@ NotificationStorage.prototype = {
         this._byTag[origin][tag] = notification;
       }
     });
     this._cached = true;
   },
 
   classID : Components.ID(NOTIFICATIONSTORAGE_CID),
   contractID : NOTIFICATIONSTORAGE_CONTRACTID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsINotificationStorage]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsINotificationStorage]),
 };
 
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NotificationStorage]);
--- a/dom/payments/test/BasiccardChromeScript.js
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -69,17 +69,17 @@ const detailedResponseUI = {
                       "",                   // payer email
                       "");                  // payer phone
     paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
   abortPayment: abortPaymentResponse,
   completePayment: completePaymentResponse,
   updatePayment: function(requestId) {
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 const simpleResponseUI = {
   showPayment: function(requestId) {
     try {
       basiccardResponseData.initData("",                 // cardholderName
                                      "4916855166538720", // cardNumber
                                      "",                 // expiryMonth
@@ -97,17 +97,17 @@ const simpleResponseUI = {
                       "",                   // payer email
                       "");                  // payer phone
     paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
   abortPayment: abortPaymentResponse,
   completePayment: completePaymentResponse,
   updatePayment: function(requestId) {
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 addMessageListener("set-detailed-ui-service", function() {
   paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-simple-ui-service", function() {
   paymentSrv.setTestingUIService(simpleResponseUI.QueryInterface(Ci.nsIPaymentUIService));
--- a/dom/payments/test/CurrencyAmountValidationChromeScript.js
+++ b/dom/payments/test/CurrencyAmountValidationChromeScript.js
@@ -16,17 +16,17 @@ const InvalidDetailsUIService = {
                            createInstance(Ci.nsIPaymentAbortActionResponse);
     abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
     paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
   completePayment: function(requestId) {
   },
   updatePayment: function(requestId) {
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 
 };
 
 function emitTestFail(message) {
   sendAsyncMessage("test-fail", message);
 }
 
 function checkLowerCaseCurrency() {
--- a/dom/payments/test/PMIValidationChromeScript.js
+++ b/dom/payments/test/PMIValidationChromeScript.js
@@ -34,17 +34,17 @@ const UIService = {
                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
                       "https://example.com",   // payment method
                       showResponseData,           // payment method data
                       "Bill A. Pacheco",          // payer name
                       "",                         // payer email
                       "");                        // payer phone
     paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 function emitTestFail(message) {
   sendAsyncMessage("test-fail", message);
 }
 
 addMessageListener("set-ui-service", function() {
   paymentSrv.setTestingUIService(UIService.QueryInterface(Ci.nsIPaymentUIService));
--- a/dom/payments/test/RequestShippingChromeScript.js
+++ b/dom/payments/test/RequestShippingChromeScript.js
@@ -66,17 +66,17 @@ const NormalUIService = {
                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
                       "testing-payment-method",   // payment method
                       showResponseData,           // payment method data
                       "",                         // payer name
                       "",                         // payer email
                       "");                        // payer phone
     paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 addMessageListener("set-normal-ui-service", function() {
   paymentSrv.setTestingUIService(NormalUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("teardown", function() {
   paymentSrv.cleanup();
--- a/dom/payments/test/ShippingOptionsChromeScript.js
+++ b/dom/payments/test/ShippingOptionsChromeScript.js
@@ -69,17 +69,17 @@ const TestingUIService = {
   completePayment: function(requestId) {
     let request = paymentSrv.getPaymentRequestById(requestId);
     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
                            createInstance(Ci.nsIPaymentCompleteActionResponse);
     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
     paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
   updatePayment: updateRequest,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 paymentSrv.setTestingUIService(TestingUIService.QueryInterface(Ci.nsIPaymentUIService));
 
 addMessageListener("set-expected-results", function(results) {
   expectedRequestOption = results.requestResult;
   expectedUpdatedOption = results.responseResult;
   changeShippingOption = results.changeOptionResult;
--- a/dom/payments/test/ShowPaymentChromeScript.js
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -145,17 +145,17 @@ function updateRequest(requestId) {
   }
 }
 
 const DummyUIService = {
   showPayment: showRequest,
   abortPayment: abortRequest,
   completePayment: completeRequest,
   updatePayment: updateRequest,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
 
 function testShowResponseInit() {
   const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
                               createInstance(Ci.nsIGeneralResponseData);
   try {
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -435,16 +435,30 @@ public:
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetTimeToNonBlankPaint();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
       GetDOMTiming()->GetTimeToNonBlankPaint(),
       mPerformance->GetRandomTimelineSeed());
   }
 
+  DOMTimeMilliSec TimeToDOMContentFlushed() const
+  {
+    if (!nsContentUtils::IsPerformanceTimingEnabled() ||
+        nsContentUtils::ShouldResistFingerprinting()) {
+      return 0;
+    }
+    if (mPerformance->IsSystemPrincipal()) {
+      return GetDOMTiming()->GetTimeToDOMContentFlushed();
+    }
+    return nsRFPService::ReduceTimePrecisionAsMSecs(
+      GetDOMTiming()->GetTimeToDOMContentFlushed(),
+      mPerformance->GetRandomTimelineSeed());
+  }
+
   PerformanceTimingData* Data() const
   {
     return mTimingData.get();
   }
 
 private:
   ~PerformanceTiming();
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -480,17 +480,19 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   if (!aDoCheckLoadURIChecks) {
     mozilla::OriginAttributes attrs =
       BasePrincipal::Cast(content->NodePrincipal())->OriginAttributesRef();
     triggeringPrincipal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
   }
 
   rv = lh->OnLinkClick(content, uri, unitarget.get(), VoidString(),
-                       aPostStream, -1, headersDataStream, true, triggeringPrincipal);
+                       aPostStream, -1, headersDataStream,
+                       /* isUserTriggered */ false,
+                       /* isTrusted */ true, triggeringPrincipal);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
 {
   nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
   if (!aDocument || !content) {
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -18,17 +18,17 @@ const PRESENTATIONTRANSPORT_CONTRACTID =
 const PRESENTATIONTRANSPORTBUILDER_CID = Components.ID("{215b2f62-46e2-4004-a3d1-6858e56c20f3}");
 const PRESENTATIONTRANSPORTBUILDER_CONTRACTID = "mozilla.org/presentation/datachanneltransportbuilder;1";
 
 function PresentationDataChannelDescription(aDataChannelSDP) {
   this._dataChannelSDP = JSON.stringify(aDataChannelSDP);
 }
 
 PresentationDataChannelDescription.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationChannelDescription]),
   get type() {
     return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL;
   },
   get tcpAddress() {
     return null;
   },
   get tcpPort() {
     return null;
@@ -41,19 +41,19 @@ PresentationDataChannelDescription.proto
 function PresentationTransportBuilder() {
   log("PresentationTransportBuilder construct");
   this._isControlChannelNeeded = true;
 }
 
 PresentationTransportBuilder.prototype = {
   classID: PRESENTATIONTRANSPORTBUILDER_CID,
   contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder,
-                                         Ci.nsIPresentationDataChannelSessionTransportBuilder,
-                                         Ci.nsITimerCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder,
+                                          Ci.nsIPresentationDataChannelSessionTransportBuilder,
+                                          Ci.nsITimerCallback]),
 
   buildDataChannelTransport: function(aRole, aWindow, aListener) {
     if (!aRole || !aWindow || !aListener) {
       log("buildDataChannelTransport with illegal parameters");
       throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (this._window) {
@@ -240,17 +240,17 @@ PresentationTransportBuilder.prototype =
 function PresentationTransport() {
   this._messageQueue = [];
   this._closeReason = Cr.NS_OK;
 }
 
 PresentationTransport.prototype = {
   classID: PRESENTATIONTRANSPORT_CID,
   contractID: PRESENTATIONTRANSPORT_CONTRACTID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransport]),
 
   init: function(aPeerConnection, aDataChannel, aWindow) {
     log("initWithDataChannel");
     this._enableDataNotification = false;
     this._dataChannel = aDataChannel;
     this._peerConnection = aPeerConnection;
     this._window = aWindow;
 
--- a/dom/presentation/PresentationNetworkHelper.js
+++ b/dom/presentation/PresentationNetworkHelper.js
@@ -9,17 +9,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const NETWORKHELPER_CID = Components.ID("{5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d}");
 
 function PresentationNetworkHelper() {}
 
 PresentationNetworkHelper.prototype = {
   classID: NETWORKHELPER_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationNetworkHelper]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationNetworkHelper]),
 
   getWifiIPAddress: function(aListener) {
     EventDispatcher.instance.sendRequestForResult({type: "Wifi:GetIPAddress"})
              .then(result => aListener.onGetWifiIPAddress(result),
                    err => aListener.onError(err));
   }
 };
 
--- a/dom/presentation/provider/AndroidCastDeviceProvider.js
+++ b/dom/presentation/provider/AndroidCastDeviceProvider.js
@@ -255,17 +255,17 @@ LocalControlChannel.prototype = {
   },
 
   reconnect: function LCC_reconnect(aPresentationId, aUrl) {
     log("1-UA on Android doesn't support reconnect.");
     throw Cr.NS_ERROR_FAILURE;
   },
 
   classID: Components.ID("{c9be9450-e5c7-4294-a287-376971b017fd}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
 };
 
 function ChromecastRemoteDisplayDevice(aProvider, aId, aName, aRole) {
   this._provider = aProvider;
   this._id       = aId;
   this._name     = aName;
   this._role     = aRole;
 }
@@ -344,20 +344,20 @@ ChromecastRemoteDisplayDevice.prototype 
           + aTopic + " data=" + aData);
       if (this.windowId === aData) {
         Services.obs.removeObserver(this, TOPIC_PRESENTATION_VIEW_READY);
         this._ctrlChannel.notifyConnected();
       }
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice,
-                                         Ci.nsIPresentationLocalDevice,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice,
+                                          Ci.nsIPresentationLocalDevice,
+                                          Ci.nsISupportsWeakReference,
+                                          Ci.nsIObserver]),
 };
 
 function AndroidCastDeviceProvider() {
   this._listener = null;
   this._deviceList = new Map();
 }
 
 AndroidCastDeviceProvider.prototype = {
@@ -458,13 +458,13 @@ AndroidCastDeviceProvider.prototype = {
         this._listener.removeDevice(device);
         this._deviceList.delete(deviceId);
         break;
       }
     }
   },
 
   classID: Components.ID("{7394f24c-dbc3-48c8-8a47-cd10169b7c6b}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIPresentationDeviceProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsIPresentationDeviceProvider]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AndroidCastDeviceProvider]);
--- a/dom/presentation/provider/PresentationControlService.js
+++ b/dom/presentation/provider/PresentationControlService.js
@@ -353,19 +353,19 @@ PresentationControlService.prototype = {
         this.startServer();
       } catch (e) {
         DEBUG && log("PresentationControlService - restart service fail: " + e); // jshint ignore:line
       }
     }
   },
 
   classID: Components.ID("{f4079b8b-ede5-4b90-a112-5b415a931deb}"),
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIServerSocketListener,
-                                          Ci.nsIPresentationControlService,
-                                          Ci.nsIObserver]),
+  QueryInterface : ChromeUtils.generateQI([Ci.nsIServerSocketListener,
+                                           Ci.nsIPresentationControlService,
+                                           Ci.nsIObserver]),
 };
 
 function ChannelDescription(aInit) {
   this._type = aInit.type;
   switch (this._type) {
     case Ci.nsIPresentationChannelDescription.TYPE_TCP:
       this._tcpAddresses = Cc["@mozilla.org/array;1"]
                            .createInstance(Ci.nsIMutableArray);
@@ -402,17 +402,17 @@ ChannelDescription.prototype = {
     return this._tcpPort;
   },
 
   get dataChannelSDP() {
     return this._dataChannelSDP;
   },
 
   classID: Components.ID("{82507aea-78a2-487e-904a-858a6c5bf4e1}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationChannelDescription]),
 };
 
 // Helper function: transfer nsIPresentationChannelDescription to json
 function discriptionAsJson(aDescription) {
   let json = {};
   json.type = aDescription.type;
   switch(aDescription.type) {
     case Ci.nsIPresentationChannelDescription.TYPE_TCP:
@@ -947,13 +947,13 @@ TCPControlChannel.prototype = {
     this._onAnswer(answer);
   },
 
   notifyIceCandidate: function(candidate) {
     this._listener.onIceCandidate(candidate);
   },
 
   classID: Components.ID("{fefb8286-0bdc-488b-98bf-0c11b485c955}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
-                                         Ci.nsIStreamListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel,
+                                          Ci.nsIStreamListener]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationControlService]); // jshint ignore:line
--- a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js
@@ -4,82 +4,82 @@
 'use strict';
 
 const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm');
 
 const manager = Cc['@mozilla.org/presentation-device/manager;1']
                   .getService(Ci.nsIPresentationDeviceManager);
 
 var testProvider = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceProvider]),
   forceDiscovery: function() {
     sendAsyncMessage('force-discovery');
   },
   listener: null,
 };
 
 var testDevice = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   establishControlChannel: function() {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
     return true;
   },
   id: null,
   name: null,
   type: null,
   listener: null,
 };
 
 var testDevice1 = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'dummyid',
   name: 'dummyName',
   type: 'dummyType',
   establishControlChannel: function(url, presentationId) {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
     return true;
   },
 };
 
 var testDevice2 = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'dummyid',
   name: 'dummyName',
   type: 'dummyType',
   establishControlChannel: function(url, presentationId) {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
     return true;
   },
 };
 
 var mockedDeviceWithoutSupportedURL = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'dummyid',
   name: 'dummyName',
   type: 'dummyType',
   establishControlChannel: function(url, presentationId) {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
     return false;
   },
 };
 
 var mockedDeviceSupportHttpsURL = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'dummyid',
   name: 'dummyName',
   type: 'dummyType',
   establishControlChannel: function(url, presentationId) {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -49,30 +49,30 @@ var sessionId = 'test-session-id-' + uui
 
 const address = Cc["@mozilla.org/supports-cstring;1"]
                   .createInstance(Ci.nsISupportsCString);
 address.data = "127.0.0.1";
 const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 addresses.appendElement(address);
 
 const mockedChannelDescription = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationChannelDescription]),
   get type() {
     if (Services.prefs.getBoolPref("dom.presentation.session_transport.data_channel.enable")) {
       return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL;
     }
     return Ci.nsIPresentationChannelDescription.TYPE_TCP;
   },
   tcpAddress: addresses,
   tcpPort: 1234,
 };
 
 const mockedServerSocket = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIServerSocket,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIServerSocket,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   get port() {
     return this._port;
@@ -94,21 +94,21 @@ const mockedServerSocket = {
     this._listener.onStopListening(this, Cr.NS_BINDING_ABORTED);
   },
   simulateOnSocketAccepted: function(serverSocket, socketTransport) {
     this._listener.onSocketAccepted(serverSocket, socketTransport);
   }
 };
 
 const mockedSocketTransport = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISocketTransport]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISocketTransport]),
 };
 
 const mockedControlChannel = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   set listener(listener) {
     this._listener = listener;
   },
   get listener() {
     return this._listener;
   },
   sendOffer: function(offer) {
     sendAsyncMessage('offer-sent', this._isValidSDP(offer));
@@ -172,33 +172,33 @@ const mockedControlChannel = {
   },
   simulateNotifyConnected: function() {
     sendAsyncMessage('control-channel-opened');
     this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyConnected();
   },
 };
 
 const mockedDevice = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'id',
   name: 'name',
   type: 'type',
   establishControlChannel: function(url, presentationId) {
     sendAsyncMessage('control-channel-established');
     return mockedControlChannel;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
     return true;
   },
 };
 
 const mockedDevicePrompt = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevicePrompt,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   set request(request) {
     this._request = request;
@@ -214,22 +214,22 @@ const mockedDevicePrompt = {
     this._request.select(mockedDevice);
   },
   simulateCancel: function(result) {
     this._request.cancel(result);
   }
 };
 
 const mockedSessionTransport = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport,
-                                         Ci.nsIPresentationSessionTransportBuilder,
-                                         Ci.nsIPresentationTCPSessionTransportBuilder,
-                                         Ci.nsIPresentationDataChannelSessionTransportBuilder,
-                                         Ci.nsIPresentationControlChannelListener,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransport,
+                                          Ci.nsIPresentationSessionTransportBuilder,
+                                          Ci.nsIPresentationTCPSessionTransportBuilder,
+                                          Ci.nsIPresentationDataChannelSessionTransportBuilder,
+                                          Ci.nsIPresentationControlChannelListener,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   set callback(callback) {
     this._callback = callback;
@@ -252,17 +252,17 @@ const mockedSessionTransport = {
     }, 0);
   },
   buildTCPReceiverTransport: function(description, listener) {
     this._listener = listener;
     this._role = Ci.nsIPresentationService.ROLE_RECEIVER;
 
     var addresses = description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpAddress;
     this._selfAddress = {
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsINetAddr]),
+      QueryInterface: ChromeUtils.generateQI([Ci.nsINetAddr]),
       address: (addresses.length > 0) ?
                 addresses.queryElementAt(0, Ci.nsISupportsCString).data : "",
       port: description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpPort,
     };
 
     setTimeout(()=>{
       this._listener.onSessionTransport(this);
       this._listener = null;
@@ -303,43 +303,43 @@ const mockedSessionTransport = {
   },
   onOffer: function(aOffer) {
   },
   onAnswer: function(aAnswer) {
   }
 };
 
 const mockedNetworkInfo = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsINetworkInfo]),
   getAddresses: function(ips, prefixLengths) {
     ips.value = ["127.0.0.1"];
     prefixLengths.value = [0];
     return 1;
   },
 };
 
 const mockedNetworkManager = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsINetworkManager,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   get activeNetworkInfo() {
     return mockedNetworkInfo;
   },
 };
 
 var requestPromise = null;
 
 const mockedRequestUIGlue = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationRequestUIGlue,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   sendRequest: function(aUrl, aSessionId) {
     sendAsyncMessage('receiver-launching', aSessionId);
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js
@@ -18,17 +18,17 @@ function debug(str) {
 }
 
 const originalFactoryData = [];
 var sessionId; // Store the uuid generated by PresentationRequest.
 var triggerControlChannelError = false; // For simulating error during control channel establishment.
 
 // control channel of sender
 const mockControlChannelOfSender = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   set listener(listener) {
     // PresentationControllingInfo::SetControlChannel
     if (listener) {
       debug('set listener for mockControlChannelOfSender without null');
     } else {
       debug('set listener for mockControlChannelOfSender with null');
     }
     this._listener = listener;
@@ -88,17 +88,17 @@ const mockControlChannelOfSender = {
     this._listener
         .QueryInterface(Ci.nsIPresentationControlChannelListener)
         .onIceCandidate(candidate);
   },
 };
 
 // control channel of receiver
 const mockControlChannelOfReceiver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   set listener(listener) {
     // PresentationPresentingInfo::SetControlChannel
     if (listener) {
       debug('set listener for mockControlChannelOfReceiver without null');
     } else {
       debug('set listener for mockControlChannelOfReceiver with null');
     }
     this._listener = listener;
@@ -153,17 +153,17 @@ const mockControlChannelOfReceiver = {
 
     this._listener
         .QueryInterface(Ci.nsIPresentationControlChannelListener)
         .onIceCandidate(candidate);
   },
 };
 
 const mockDevice = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id:   'id',
   name: 'name',
   type: 'type',
   establishControlChannel: function(url, presentationId) {
     if (triggerControlChannelError) {
       throw Cr.NS_ERROR_FAILURE;
     }
     sendAsyncMessage('control-channel-established');
@@ -173,18 +173,18 @@ const mockDevice = {
     sendAsyncMessage('device-disconnected');
   },
   isRequestedUrlSupported: function(requestedUrl) {
     return true;
   },
 };
 
 const mockDevicePrompt = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevicePrompt,
+                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(aIID);
   },
   set request(request) {
     this._request = request;
@@ -200,18 +200,18 @@ const mockDevicePrompt = {
     this._request.select(mockDevice);
   },
   simulateCancel: function() {
     this._request.cancel();
   }
 };
 
 const mockRequestUIGlue = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue,
-                                         Ci.nsIFactory]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationRequestUIGlue,
+                                          Ci.nsIFactory]),
   set promise(aPromise) {
     this._promise = aPromise
   },
   get promise() {
     return this._promise;
   },
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
--- a/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html
+++ b/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html
@@ -37,17 +37,17 @@ var isClientReady = false;
 var isServerReady = false;
 var isClientClosed = false;
 var isServerClosed = false;
 
 var gResolve;
 var gReject;
 
 const clientCallback = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
   notifyTransportReady: function () {
     info("Client transport ready.");
 
     isClientReady = true;
     if (isClientReady && isServerReady) {
       gResolve();
     }
   },
@@ -61,17 +61,17 @@ const clientCallback = {
   },
   notifyData: function(aData) {
     is(aData, serverMessage, "Client transport receives data.");
     gResolve();
   },
 };
 
 const serverCallback = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
   notifyTransportReady: function () {
     info("Server transport ready.");
 
     isServerReady = true;
     if (isClientReady && isServerReady) {
       gResolve();
     }
   },
@@ -85,17 +85,17 @@ const serverCallback = {
   },
   notifyData: function(aData) {
     is(aData, clientMessage, "Server transport receives data.");
     gResolve()
   },
 };
 
 const clientListener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
   onSessionTransport: function(aTransport) {
     info("Client Transport is built.");
     clientTransport = aTransport;
     clientTransport.callback = clientCallback;
   },
   onError: function(aError)  {
     ok(false, "client's builder reports error " + aError);
   },
@@ -116,17 +116,17 @@ const clientListener = {
     this._remoteBuilder = aRemoteBuilder;
   },
   set localBuilder(aLocalBuilder) {
     this._localBuilder = aLocalBuilder;
   },
 }
 
 const serverListener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
   onSessionTransport: function(aTransport) {
     info("Server Transport is built.");
     serverTransport = aTransport;
     serverTransport.callback = serverCallback;
     serverTransport.enableDataNotification();
   },
   onError: function(aError)  {
     ok(false, "server's builder reports error " + aError);
--- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
+++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
@@ -59,17 +59,17 @@ MockFactory.prototype = {
         return this._cls.QueryInterface(aIID);
       default:
         return null;
     }
   },
   lockFactory: function(aLock) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory])
 };
 
 function ContractHook(aContractID, aClass) {
   this._contractID = aContractID;
   this.classID = Cc[UUID_CONTRACT_ID].getService(Ci.nsIUUIDGenerator).generateUUID();
   this._newFactory = new MockFactory(aClass);
 
   if (!this.hookedMap.has(this._contractID)) {
@@ -127,17 +127,17 @@ ContractHook.prototype = {
     }
 
     return { classID: classID, factory: factory };
   }
 };
 
 function MockDNSServiceInfo() {}
 MockDNSServiceInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceInfo]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceInfo]),
 
   set host(aHost) {
     this._host = aHost;
   },
 
   get host() {
     return this._host;
   },
@@ -190,18 +190,18 @@ MockDNSServiceInfo.prototype = {
     return this._attributes;
   }
 };
 
 function TestPresentationDeviceListener() {
   this.devices = {};
 }
 TestPresentationDeviceListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                         Ci.nsISupportsWeakReference]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                          Ci.nsISupportsWeakReference]),
 
   addDevice: function(device) { this.devices[device.id] = device; },
   removeDevice: function(device) { delete this.devices[device.id]; },
   updateDevice: function(device) { this.devices[device.id] = device; },
   onSessionRequest: function(device, url, presentationId, controlChannel) {},
 
   count: function() {
     var size = 0, key;
@@ -227,23 +227,23 @@ function createDevice(host, port, servic
 }
 
 function registerService() {
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
 
   let deferred = Promise.defer();
 
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {
       deferred.resolve();
       this.serviceRegistered++;
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: () => {
           this.serviceUnregistered++;
         }
       };
     },
     resolveService: function(serviceInfo, listener) {},
     serviceRegistered: 0,
     serviceUnregistered: 0
@@ -251,18 +251,18 @@ function registerService() {
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
 
   Assert.equal(mockObj.serviceRegistered, 0);
   Assert.equal(mockObj.serviceUnregistered, 0);
 
   // Register
   provider.listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
   };
 
   deferred.promise.then(function() {
     Assert.equal(mockObj.serviceRegistered, 1);
     Assert.equal(mockObj.serviceUnregistered, 0);
@@ -277,32 +277,32 @@ function registerService() {
 }
 
 function noRegisterService() {
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, false);
 
   let deferred = Promise.defer();
 
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {
       deferred.resolve();
       Assert.ok(false, "should not register service if not discoverable");
     },
     resolveService: function(serviceInfo, listener) {},
   };
 
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
 
   // Try register
   provider.listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
   };
 
   let race = Promise.race([
     deferred.promise,
     sleep(1000),
@@ -316,23 +316,23 @@ function noRegisterService() {
 }
 
 function registerServiceDynamically() {
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, false);
 
   let deferred = Promise.defer();
 
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {
       deferred.resolve();
       this.serviceRegistered++;
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: () => {
           this.serviceUnregistered++;
         }
       };
     },
     resolveService: function(serviceInfo, listener) {},
     serviceRegistered: 0,
     serviceUnregistered: 0
@@ -340,18 +340,18 @@ function registerServiceDynamically() {
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
 
   Assert.equal(mockObj.serviceRegistered, 0);
   Assert.equal(mockObj.serviceRegistered, 0);
 
   // Try Register
   provider.listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
   };
 
   Assert.equal(mockObj.serviceRegistered, 0);
   Assert.equal(mockObj.serviceUnregistered, 0);
 
@@ -379,25 +379,25 @@ function registerServiceDynamically() {
 function addDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
       listener.onServiceResolved(createDevice(mockDevice.host,
@@ -429,25 +429,25 @@ function addDevice() {
 function filterDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
       listener.onServiceResolved(createDevice(mockDevice.host,
@@ -455,18 +455,18 @@ function filterDevice() {
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {
       let tests = [
         { requestedUrl: "app://fling-player.gaiamobile.org/index.html", supported: true },
         { requestedUrl: "app://notification-receiver.gaiamobile.org/index.html", supported: true },
         { requestedUrl: "http://example.com", supported: true },
         { requestedUrl: "https://example.com", supported: true },
         { requestedUrl: "ftp://example.com", supported: false },
         { requestedUrl: "app://unknown-app-id", supported: false },
@@ -499,60 +499,60 @@ function handleSessionRequest() {
 
   Services.prefs.setCharPref(PREF_DEVICENAME, testDeviceName);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       listener.onServiceResolved(createDevice(mockDevice.host,
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     connect: function(deviceInfo) {
       this.request = {
         deviceInfo: deviceInfo,
       };
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
       };
     },
     id: "",
     version: LATEST_VERSION,
     isCompatibleServer: function(version) {
       return this.version === version;
     }
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
   let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {
       this.device = device;
     },
   };
 
   provider.listener = listener;
 
   let controlChannel = listener.device.establishControlChannel();
@@ -571,80 +571,80 @@ function handleOnSessionRequest() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       listener.onServiceResolved(createDevice(mockDevice.host,
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     startServer: function() {},
     sessionRequest: function() {},
     close: function() {},
     id: '',
     version: LATEST_VERSION,
     port: 0,
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
   let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
     onSessionRequest: function(device, url, presentationId, controlChannel) {
       Assert.ok(true, "receive onSessionRequest event");
       this.request = {
         deviceId: device.id,
         url: url,
         presentationId: presentationId,
       };
     }
   };
 
   provider.listener = listener;
 
   const deviceInfo = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPDeviceInfo]),
     id: mockDevice.host,
     address: mockDevice.host,
     port: 54321,
   };
 
   const testUrl = "http://example.com";
   const testPresentationId = "test-presentation-id";
   const testControlChannel = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   };
   provider.QueryInterface(Ci.nsIPresentationControlServerListener)
           .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel);
 
   Assert.equal(listener.request.deviceId, deviceInfo.id);
   Assert.equal(listener.request.url, testUrl);
   Assert.equal(listener.request.presentationId, testPresentationId);
 
@@ -653,39 +653,39 @@ function handleOnSessionRequest() {
   run_next_test();
 }
 
 function handleOnSessionRequestFromUnknownDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
 
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {}
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     startServer: function() {},
     sessionRequest: function() {},
     close: function() {},
     id: '',
     version: LATEST_VERSION,
     port: 0,
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
   let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {
       Assert.ok(false, "shouldn't create any new device");
     },
     removeDevice: function(device) {
       Assert.ok(false, "shouldn't remote any device");
     },
     updateDevice: function(device) {
       Assert.ok(false, "shouldn't update any device");
@@ -698,26 +698,26 @@ function handleOnSessionRequestFromUnkno
         presentationId: presentationId,
       };
     }
   };
 
   provider.listener = listener;
 
   const deviceInfo = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPDeviceInfo]),
     id: "unknown-device.local",
     address: "unknown-device.local",
     port: 12345,
   };
 
   const testUrl = "http://example.com";
   const testPresentationId = "test-presentation-id";
   const testControlChannel = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   };
   provider.QueryInterface(Ci.nsIPresentationControlServerListener)
           .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel);
 
   Assert.equal(listener.request.deviceId, deviceInfo.id);
   Assert.equal(listener.request.url, testUrl);
   Assert.equal(listener.request.presentationId, testPresentationId);
 
@@ -726,30 +726,30 @@ function handleOnSessionRequestFromUnkno
   run_next_test();
 }
 
 function noAddDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
 
   let mockDevice = createDevice("device.local", 12345, "service.name", SERVICE_TYPE);
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       Assert.ok(false, "shouldn't perform any device discovery");
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
     }
   };
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
 
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
   };
   provider.listener = listener;
   provider.forceDiscovery();
   provider.listener = null;
 
@@ -763,51 +763,51 @@ function ignoreIncompatibleDevice() {
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
 
   let deferred = Promise.defer();
 
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {
       deferred.resolve();
       listener.onServiceRegistered(createDevice("",
                                                 54321,
                                                 mockDevice.serviceName,
                                                 mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
       listener.onServiceResolved(createDevice(mockDevice.host,
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     startServer: function() {
       Services.tm.dispatchToMainThread(() => {
         this.listener.onServerReady(this.port, this.certFingerprint);
       });
     },
     sessionRequest: function() {},
     close: function() {},
     id: '',
@@ -848,51 +848,51 @@ function ignoreSelfDevice() {
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
 
   let deferred = Promise.defer();
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {
       deferred.resolve();
       listener.onServiceRegistered(createDevice("",
                                                 0,
                                                 mockDevice.serviceName,
                                                 mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
       listener.onServiceResolved(createDevice(mockDevice.host,
                                               mockDevice.port,
                                               mockDevice.serviceName,
                                               mockDevice.serviceType));
     }
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     startServer: function() {
       Services.tm.dispatchToMainThread(() => {
         this.listener.onServerReady(this.port, this.certFingerprint);
       });
     },
     sessionRequest: function() {},
     close: function() {},
     id: '',
@@ -929,25 +929,25 @@ function ignoreSelfDevice() {
 function addDeviceDynamically() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
       listener.onServiceResolved(createDevice(mockDevice.host,
@@ -986,29 +986,29 @@ function updateDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
 
   let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE);
   let mockDevice2 = createDevice("A.local", 23456, "N2", SERVICE_TYPE);
 
   let mockObj = {
     discovered: false,
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
 
       if (!this.discovered) {
         listener.onServiceFound(mockDevice1);
       } else {
         listener.onServiceFound(mockDevice2);
       }
       this.discovered = true;
 
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {
           listener.onDiscoveryStopped(serviceType);
         }
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceType, SERVICE_TYPE);
@@ -1020,18 +1020,18 @@ function updateDevice() {
         Assert.ok(false);
       }
     }
   };
 
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
 
     addDevice: function(device) {
       Assert.ok(!this.isDeviceAdded);
       Assert.equal(device.id, mockDevice1.host);
       Assert.equal(device.name, mockDevice1.serviceName);
       this.isDeviceAdded = true;
     },
     removeDevice: function(device) { Assert.ok(false); },
@@ -1073,31 +1073,31 @@ function diffDiscovery() {
 
   let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE);
   let mockDevice2 = createDevice("B.local", 23456, "N2", SERVICE_TYPE);
   let mockDevice3 = createDevice("C.local", 45678, "N3", SERVICE_TYPE);
 
   let mockObj = {
     discovered: false,
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
 
       if (!this.discovered) {
         listener.onServiceFound(mockDevice1);
         listener.onServiceFound(mockDevice2);
       } else {
         listener.onServiceFound(mockDevice1);
         listener.onServiceFound(mockDevice3);
       }
       this.discovered = true;
 
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {
           listener.onDiscoveryStopped(serviceType);
         }
       };
     },
     registerService: function(serviceInfo, listener) {},
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceType, SERVICE_TYPE);
@@ -1155,32 +1155,32 @@ function serverClosed() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, true);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 SERVICE_TYPE);
 
   let mockObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {
       listener.onDiscoveryStarted(serviceType);
       listener.onServiceFound(createDevice("",
                                            0,
                                            mockDevice.serviceName,
                                            mockDevice.serviceType));
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: function() {}
       };
     },
     registerService: function(serviceInfo, listener) {
       this.serviceRegistered++;
       return {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
         cancel: () => {
           this.serviceUnregistered++;
         }
       };
     },
     resolveService: function(serviceInfo, listener) {
       Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
       Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
@@ -1195,18 +1195,18 @@ function serverClosed() {
   let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
 
   Assert.equal(mockObj.serviceRegistered, 0);
   Assert.equal(mockObj.serviceUnregistered, 0);
 
   // Register
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) { this.devices.push(device); },
     removeDevice: function(device) {},
     updateDevice: function(device) {},
     devices: []
   };
   Assert.equal(listener.devices.length, 0);
 
   provider.listener = listener;
@@ -1233,28 +1233,28 @@ function serverClosed() {
 
 function serverRetry() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
 
   let isRetrying = false;
 
   let mockSDObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
     startDiscovery: function(serviceType, listener) {},
     registerService: function(serviceInfo, listener) {
       Assert.ok(isRetrying, "register service after retrying startServer");
       provider.listener = null;
       run_next_test();
     },
     resolveService: function(serviceInfo, listener) {}
   };
 
   let mockServerObj = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlService]),
     startServer: function(encrypted, port) {
       if (!isRetrying) {
         isRetrying = true;
         Services.tm.dispatchToMainThread(() => {
           this.listener.onServerStopped(Cr.NS_ERROR_FAILURE);
         });
       } else {
         this.port = 54321;
@@ -1271,18 +1271,18 @@ function serverRetry() {
     certFingerprint: 'mock-cert-fingerprint',
     listener: null,
   };
 
   let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
   let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
   let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
   let listener = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
-                                           Ci.nsISupportsWeakReference]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceListener,
+                                            Ci.nsISupportsWeakReference]),
     addDevice: function(device) {},
     removeDevice: function(device) {},
     updateDevice: function(device) {},
     onSessionRequest: function(device, url, presentationId, controlChannel) {}
   };
 
   provider.listener = listener;
 }
--- a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js
+++ b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js
@@ -11,41 +11,41 @@ const manager = Cc['@mozilla.org/present
                   .getService(Ci.nsIPresentationDeviceManager);
 
 function TestPresentationDevice() {}
 
 
 function TestPresentationControlChannel() {}
 
 TestPresentationControlChannel.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannel]),
   sendOffer: function(offer) {},
   sendAnswer: function(answer) {},
   disconnect: function() {},
   launch: function() {},
   terminate: function() {},
   reconnect: function() {},
   set listener(listener) {},
   get listener() {},
 };
 
 var testProvider = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDeviceProvider]),
 
   forceDiscovery: function() {
   },
   set listener(listener) {
   },
   get listener() {
   },
 };
 
 const forbiddenRequestedUrl = 'http://example.com';
 var testDevice = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationDevice]),
   id: 'id',
   name: 'name',
   type: 'type',
   establishControlChannel: function(url, presentationId) {
     return null;
   },
   disconnect: function() {},
   isRequestedUrlSupported: function(requestedUrl) {
--- a/dom/presentation/tests/xpcshell/test_presentation_session_transport.js
+++ b/dom/presentation/tests/xpcshell/test_presentation_session_transport.js
@@ -24,28 +24,28 @@ const serverMessage = "Server Message";
 
 const address = Cc["@mozilla.org/supports-cstring;1"]
                   .createInstance(Ci.nsISupportsCString);
 address.data = "127.0.0.1";
 const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 addresses.appendElement(address);
 
 const serverChannelDescription = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationChannelDescription]),
   type: 1,
   tcpAddress: addresses,
 };
 
 var isClientReady = false;
 var isServerReady = false;
 var isClientClosed = false;
 var isServerClosed = false;
 
 const clientCallback = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
   notifyTransportReady: function () {
     Assert.ok(true, "Client transport ready.");
 
     isClientReady = true;
     if (isClientReady && isServerReady) {
       run_next_test();
     }
   },
@@ -59,17 +59,17 @@ const clientCallback = {
   },
   notifyData: function(aData) {
     Assert.equal(aData, serverMessage, "Client transport receives data.");
     run_next_test();
   },
 };
 
 const serverCallback = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
   notifyTransportReady: function () {
     Assert.ok(true, "Server transport ready.");
 
     isServerReady = true;
     if (isClientReady && isServerReady) {
       run_next_test();
     }
   },
@@ -83,30 +83,30 @@ const serverCallback = {
   },
   notifyData: function(aData) {
     Assert.equal(aData, clientMessage, "Server transport receives data.");
     run_next_test();
   },
 };
 
 const clientListener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
   onSessionTransport(aTransport) {
     Assert.ok(true, "Client Transport is built.");
     clientTransport = aTransport;
     clientTransport.callback = clientCallback;
 
     if (serverTransport) {
       run_next_test();
     }
   }
 }
 
 const serverListener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
   onSessionTransport(aTransport) {
     Assert.ok(true, "Server Transport is built.");
     serverTransport = aTransport;
     serverTransport.callback = serverCallback;
     serverTransport.enableDataNotification();
 
     if (clientTransport) {
       run_next_test();
--- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
+++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
@@ -31,17 +31,17 @@ function TestDescription(aType, aTcpAddr
                     .createInstance(Ci.nsISupportsCString);
     wrapper.data = address;
     this.tcpAddress.appendElement(wrapper);
   }
   this.tcpPort = aTcpPort;
 }
 
 TestDescription.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationChannelDescription]),
 }
 
 const CONTROLLER_CONTROL_CHANNEL_PORT = 36777;
 const PRESENTER_CONTROL_CHANNEL_PORT = 36888;
 
 var CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_OK;
 var candidate;
 
@@ -121,34 +121,34 @@ function testPresentationServer() {
           this.status = 'opened';
         },
         notifyDisconnected: function(aReason) {
           Assert.equal(this.status, 'onOffer', '4. presenterControlChannel: closed');
           Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'presenterControlChannel notify closed');
           this.status = 'closed';
           yayFuncs.controllerControlChannelClose();
         },
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
       };
     },
     onReconnectRequest: function(deviceInfo, url, presentationId, controlChannel) {
       Assert.equal(url, 'http://example.com', 'expected url');
       Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
       yayFuncs.presenterControlChannelReconnect();
     },
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlServerListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlServerListener]),
   };
 
   let presenterDeviceInfo = {
     id: 'presentatorID',
     address: '127.0.0.1',
     port: PRESENTER_CONTROL_CHANNEL_PORT,
     certFingerprint: pcs.certFingerprint,
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPDeviceInfo]),
   };
 
   let controllerControlChannel = pcs.connect(presenterDeviceInfo);
 
   controllerControlChannel.listener = {
     status: 'created',
     onOffer: function(offer) {
       Assert.ok(false, 'get offer');
@@ -191,20 +191,20 @@ function testPresentationServer() {
       let reconnectControllerControlChannel = pcs.connect(presenterDeviceInfo);
       reconnectControllerControlChannel.listener = {
         notifyConnected: function() {
           reconnectControllerControlChannel.reconnect('testPresentationId', 'http://example.com');
         },
         notifyReconnected: function() {
           yayFuncs.controllerControlChannelReconnect();
         },
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
       };
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
   };
 }
 
 function terminateRequest() {
   let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
                                    'controllerControlChannelDisconnected',
                                    'presenterControlChannelDisconnected',
                                    'terminatedByController',
@@ -231,47 +231,47 @@ function terminateRequest() {
 
             terminatePhase = 'receiver';
             controllerControlChannel.terminate('testPresentationId');
           },
           notifyDisconnected: function(aReason) {
             Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify disconncted');
             yayFuncs.controllerControlChannelDisconnected();
           },
-          QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+          QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
         };
       } else {
         Assert.equal(deviceInfo.id, presenterDeviceInfo.id, 'expected presenter device id');
         Assert.equal(isFromReceiver, true, 'expected request from receiver');
         yayFuncs.terminatedByReceiver();
         presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON);
       }
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPPresentationServerListener]),
   };
 
   let presenterDeviceInfo = {
     id: 'presentatorID',
     address: '127.0.0.1',
     port: PRESENTER_CONTROL_CHANNEL_PORT,
     certFingerprint: pcs.certFingerprint,
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPDeviceInfo]),
   };
 
   let presenterControlChannel = pcs.connect(presenterDeviceInfo);
 
   presenterControlChannel.listener = {
     notifyConnected: function() {
       presenterControlChannel.terminate('testPresentationId');
     },
     notifyDisconnected: function(aReason) {
       Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify disconnected');
       yayFuncs.presenterControlChannelDisconnected();
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
   };
 }
 
 function terminateRequestAbnormal() {
   let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
                                    'controllerControlChannelDisconnected',
                                    'presenterControlChannelDisconnected']);
   let controllerControlChannel;
@@ -290,41 +290,41 @@ function terminateRequestAbnormal() {
           notifyConnected: function() {
           Assert.ok(true, 'control channel notify connected');
           yayFuncs.controllerControlChannelConnected();
         },
         notifyDisconnected: function(aReason) {
           Assert.equal(aReason, Cr.NS_ERROR_FAILURE, 'controllerControlChannel notify disconncted with error');
           yayFuncs.controllerControlChannelDisconnected();
         },
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
       };
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPPresentationServerListener]),
   };
 
   let presenterDeviceInfo = {
     id: 'presentatorID',
     address: '127.0.0.1',
     port: PRESENTER_CONTROL_CHANNEL_PORT,
     certFingerprint: pcs.certFingerprint,
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsITCPDeviceInfo]),
   };
 
   let presenterControlChannel = pcs.connect(presenterDeviceInfo);
 
   presenterControlChannel.listener = {
     notifyConnected: function() {
       presenterControlChannel.terminate('testPresentationId');
     },
     notifyDisconnected: function(aReason) {
       Assert.equal(aReason, Cr.NS_ERROR_FAILURE, '4. presenterControlChannel notify disconnected with error');
       yayFuncs.presenterControlChannelDisconnected();
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
   };
 }
 
 function setOffline() {
   pcs.listener = {
     onServerReady: function(aPort, aCertFingerprint) {
       Assert.notEqual(aPort, 0, 'TCPPresentationServer port changed and the port should be valid');
       pcs.close();
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -32,19 +32,19 @@ function Push() {
 
 Push.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   contractID: "@mozilla.org/push/PushManager;1",
 
   classID : PUSH_CID,
 
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
-                                          Ci.nsISupportsWeakReference,
-                                          Ci.nsIObserver]),
+  QueryInterface : ChromeUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
+                                           Ci.nsISupportsWeakReference,
+                                           Ci.nsIObserver]),
 
   init: function(win) {
     console.debug("init()");
 
     this._window = win;
 
     this.initDOMRequestHelper(win);
 
@@ -177,26 +177,26 @@ Push.prototype = {
   },
 
   _requestPermission: function(allowCallback, cancelCallback) {
     // Create an array with a single nsIContentPermissionType element.
     let type = {
       type: "desktop-notification",
       access: null,
       options: [],
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]),
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
     };
     let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     typeArray.appendElement(type);
 
     // create a nsIContentPermissionRequest
     let request = {
       types: typeArray,
       principal: this._principal,
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
       allow: allowCallback,
       cancel: cancelCallback,
       window: this._window,
     };
 
     // Using askPermission from nsIDOMWindowUtils that takes care of the
     // remoting if needed.
     let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -207,17 +207,17 @@ Push.prototype = {
 
 function PushSubscriptionCallback(pushManager, resolve, reject) {
   this.pushManager = pushManager;
   this.resolve = resolve;
   this.reject = reject;
 }
 
 PushSubscriptionCallback.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushSubscriptionCallback]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPushSubscriptionCallback]),
 
   onPushSubscription: function(ok, subscription) {
     let {pushManager} = this;
     if (!Components.isSuccessCode(ok)) {
       this._rejectWithError(ok);
       return;
     }
 
--- a/dom/push/PushComponents.js
+++ b/dom/push/PushComponents.js
@@ -44,17 +44,17 @@ const OBSERVER_TOPIC_SUBSCRIPTION_MODIFI
 function PushServiceBase() {
   this.wrappedJSObject = this;
   this._addListeners();
 }
 
 PushServiceBase.prototype = {
   classID: Components.ID("{daaa8d73-677e-4233-8acd-2c404bd01658}"),
   contractID: "@mozilla.org/push/Service;1",
-  QueryInterface: XPCOMUtils.generateQI([
+  QueryInterface: ChromeUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
     Ci.nsIPushService,
     Ci.nsIPushQuotaManager,
     Ci.nsIPushErrorReporter,
   ]),
 
   pushTopic: OBSERVER_TOPIC_PUSH,
@@ -454,17 +454,17 @@ Object.assign(PushServiceContent.prototy
 });
 
 /** `PushSubscription` instances are passed to all subscription callbacks. */
 function PushSubscription(props) {
   this._props = props;
 }
 
 PushSubscription.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushSubscription]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPushSubscription]),
 
   /** The URL for sending messages to this subscription. */
   get endpoint() {
     return this._props.endpoint;
   },
 
   /** The last time a message was sent to this subscription. */
   get lastPush() {
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -45,23 +45,18 @@ const kPUSHHTTP2DB_STORE_NAME = "pushHtt
 var PushSubscriptionListener = function(pushService, uri) {
   console.debug("PushSubscriptionListener()");
   this._pushService = pushService;
   this.uri = uri;
 };
 
 PushSubscriptionListener.prototype = {
 
-  QueryInterface: function (aIID) {
-    if (aIID.equals(Ci.nsIHttpPushListener) ||
-        aIID.equals(Ci.nsIStreamListener)) {
-      return this;
-    }
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: ChromeUtils.generateQI(["nsIHttpPushListener",
+                                         "nsIStreamListener"]),
 
   getInterface: function(aIID) {
     return this.QueryInterface(aIID);
   },
 
   onStartRequest: function(aRequest, aContext) {
     console.debug("PushSubscriptionListener: onStartRequest()");
     // We do not do anything here.
--- a/dom/push/test/mockpushserviceparent.js
+++ b/dom/push/test/mockpushserviceparent.js
@@ -25,20 +25,17 @@ function MockWebSocketParent(originalURI
 }
 
 MockWebSocketParent.prototype = {
   _originalURI: null,
 
   _listener: null,
   _context: null,
 
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsISupports,
-    Ci.nsIWebSocketChannel
-  ]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebSocketChannel]),
 
   get originalURI() {
     return this._originalURI;
   },
 
   asyncOpen(uri, origin, windowId, listener, context) {
     this._listener = listener;
     this._context = context;
--- a/dom/push/test/xpcshell/PushServiceHandler.js
+++ b/dom/push/test/xpcshell/PushServiceHandler.js
@@ -14,16 +14,16 @@ function PushServiceHandler() {
   this.observed = [];
   Services.obs.addObserver(this, pushService.pushTopic);
   Services.obs.addObserver(this, pushService.subscriptionChangeTopic);
   Services.obs.addObserver(this, pushService.subscriptionModifiedTopic);
 }
 
 PushServiceHandler.prototype = {
   classID: Components.ID("{bb7c5199-c0f7-4976-9f6d-1306e32c5591}"),
-  QueryInterface: XPCOMUtils.generateQI([]),
+  QueryInterface: ChromeUtils.generateQI([]),
 
   observe(subject, topic, data) {
     this.observed.push({ subject, topic, data });
   },
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushServiceHandler]);
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -193,20 +193,17 @@ MockWebSocket.prototype = {
   _onRegister: null,
   _onUnregister: null,
   _onACK: null,
   _onPing: null,
 
   _listener: null,
   _context: null,
 
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsISupports,
-    Ci.nsIWebSocketChannel
-  ]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebSocketChannel]),
 
   get originalURI() {
     return this._originalURI;
   },
 
   asyncOpen(uri, origin, windowId, listener, context) {
     this._listener = listener;
     this._context = context;
--- a/dom/push/test/xpcshell/test_permissions.js
+++ b/dom/push/test/xpcshell/test_permissions.js
@@ -24,17 +24,17 @@ function run_test() {
 let unregisterDefers = {};
 
 function promiseUnregister(keyID) {
   return new Promise(r => unregisterDefers[keyID] = r);
 }
 
 function makePushPermission(url, capability) {
   return {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPermission]),
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIPermission]),
     capability: Ci.nsIPermissionManager[capability],
     expireTime: 0,
     expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
     principal: Services.scriptSecurityManager.createCodebasePrincipal(
       Services.io.newURI(url), {}
     ),
     type: 'desktop-notification',
   };
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -206,27 +206,27 @@ function WifiGeoCoordsObject(lat, lon, a
   this.latitude = lat;
   this.longitude = lon;
   this.accuracy = acc;
   this.altitude = alt;
   this.altitudeAccuracy = altacc;
 }
 
 WifiGeoCoordsObject.prototype = {
-  QueryInterface:  XPCOMUtils.generateQI([Ci.nsIDOMGeoPositionCoords])
+  QueryInterface:  ChromeUtils.generateQI([Ci.nsIDOMGeoPositionCoords])
 };
 
 function WifiGeoPositionObject(lat, lng, acc) {
   this.coords = new WifiGeoCoordsObject(lat, lng, acc, 0, 0);
   this.address = null;
   this.timestamp = Date.now();
 }
 
 WifiGeoPositionObject.prototype = {
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition])
+  QueryInterface:   ChromeUtils.generateQI([Ci.nsIDOMGeoPosition])
 };
 
 function WifiGeoPositionProvider() {
   try {
     gLoggingEnabled = Services.prefs.getBoolPref("geo.wifi.logging.enabled");
   } catch (e) {}
 
   try {
@@ -239,20 +239,20 @@ function WifiGeoPositionProvider() {
 
   this.wifiService = null;
   this.timer = null;
   this.started = false;
 }
 
 WifiGeoPositionProvider.prototype = {
   classID:          Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
-                                           Ci.nsIWifiListener,
-                                           Ci.nsITimerCallback,
-                                           Ci.nsIObserver]),
+  QueryInterface:   ChromeUtils.generateQI([Ci.nsIGeolocationProvider,
+                                            Ci.nsIWifiListener,
+                                            Ci.nsITimerCallback,
+                                            Ci.nsIObserver]),
   listener: null,
 
   resetTimer: function() {
     if (this.timer) {
       this.timer.cancel();
       this.timer = null;
     }
     // wifi thread triggers WifiGeoPositionProvider to proceed, with no wifi, do manual timeout
--- a/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
+++ b/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
@@ -5,17 +5,17 @@ const ConsoleAPIStorage = Cc["@mozilla.o
       .getService(Ci.nsIConsoleAPIStorage);
 
 const {WebExtensionPolicy} = Cu.getGlobalForObject(ChromeUtils.import("resource://gre/modules/Services.jsm", {}));
 
 const FAKE_ADDON_ID = "test-webext-addon@mozilla.org";
 const EXPECTED_CONSOLE_ID = `addon/${FAKE_ADDON_ID}`;
 const EXPECTED_CONSOLE_MESSAGE_CONTENT = "fake-webext-addon-test-log-message";
 const ConsoleObserver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   init() {
     Services.obs.addObserver(this, "console-api-log-event");
   },
 
   uninit() {
     Services.obs.removeObserver(this, "console-api-log-event");
   },
--- a/dom/tests/browser/browser_ConsoleStorageAPITests.js
+++ b/dom/tests/browser/browser_ConsoleStorageAPITests.js
@@ -28,17 +28,17 @@ add_task(async function()
   let observerPromise = ContentTask.spawn(browser, null, async function(opt) {
     const TEST_URI = "http://example.com/browser/dom/tests/browser/test-console-api.html";
     let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
           .getService(Ci.nsIConsoleAPIStorage);
 
     let observerPromise = new Promise(resolve => {
       let apiCallCount = 0;
       let ConsoleObserver = {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
         observe: function(aSubject, aTopic, aData) {
           if (aTopic == "console-storage-cache-event") {
             apiCallCount++;
             if (apiCallCount == 4) {
               let windowId = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 
--- a/dom/tests/browser/browser_bug1004814.js
+++ b/dom/tests/browser/browser_bug1004814.js
@@ -5,17 +5,17 @@
 
 add_task(async function() {
   await BrowserTestUtils.withNewTab("about:blank", async function(aBrowser) {
     let duration = await ContentTask.spawn(aBrowser, null, function (opts) {
       const TEST_URI = "http://example.com/browser/dom/tests/browser/test_bug1004814.html";
 
       return new Promise(resolve => {
         let ConsoleObserver = {
-          QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+          QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
           observe: function(aSubject, aTopic, aData) {
             var obj = aSubject.wrappedJSObject;
             if (obj.arguments.length != 1 || obj.arguments[0] != 'bug1004814' ||
                 obj.level != 'timeEnd') {
               return;
             }
 
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -784,18 +784,16 @@ var interfaceNamesInGlobalScope =
     {name: "PluginArray", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PointerEvent", insecureContext: true, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PopStateEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PopupBlockedEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PopupBoxObject", insecureContext: true, xbl: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Presentation", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationAvailability", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationConnection", insecureContext: true, desktop: false, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationConnectionAvailableEvent", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -1264,16 +1262,18 @@ var interfaceNamesInGlobalScope =
     {name: "XSLTProcessor", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULCommandEvent", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULDocument", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "XULPopupElement", insecureContext: true, xbl: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
 function createInterfaceMap(isXBLScope) {
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
--- a/dom/webidl/PerformanceTiming.webidl
+++ b/dom/webidl/PerformanceTiming.webidl
@@ -34,10 +34,16 @@ interface PerformanceTiming {
   readonly attribute unsigned long long loadEventEnd;
 
   // This is a Chrome proprietary extension and not part of the
   // performance/navigation timing specification.
   // Returns 0 if a non-blank paint has not happened.
   [Pref="dom.performance.time_to_non_blank_paint.enabled"]
   readonly attribute unsigned long long timeToNonBlankPaint;
 
+  // This is a Mozilla proprietary extension and not part of the
+  // performance/navigation timing specification. It marks the
+  // completion of the first presentation flush after DOMContentLoaded.
+  [Pref="dom.performance.time_to_dom_content_flushed.enabled"]
+  readonly attribute unsigned long long timeToDOMContentFlushed;
+
   jsonifier;
 };
rename from dom/webidl/PopupBoxObject.webidl
rename to dom/webidl/XULPopupElement.webidl
--- a/dom/webidl/PopupBoxObject.webidl
+++ b/dom/webidl/XULPopupElement.webidl
@@ -1,50 +1,39 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-[Func="IsChromeOrXBL"]
-interface PopupBoxObject : BoxObject
+dictionary OpenPopupOptions {
+  // manner in which to anchor the popup to node
+  DOMString position = "";
+  // horizontal offset
+  long x = 0;
+  // vertical offset
+  long y = 0;
+  // isContextMenu true for context menus, false for other popups
+  boolean isContextMenu = false;
+  // true if popup node attributes override position
+  boolean attributesOverride = false;
+  // triggerEvent the event that triggered this popup (mouse click for example)
+  Event? triggerEvent = null;
+};
+
+typedef (DOMString or OpenPopupOptions) StringOrOpenPopupOptions;
+
+[HTMLConstructor, Func="IsChromeOrXBL"]
+interface XULPopupElement : XULElement
 {
   /**
-   *  This method is deprecated. Use openPopup or openPopupAtScreen instead.
-   */
-  void showPopup(Element? srcContent, Element popupContent,
-                 long xpos, long ypos,
-                 optional DOMString popupType = "",
-                 optional DOMString anchorAlignment = "",
-                 optional DOMString popupAlignment = "");
-
-  /**
-   *  Hide the popup if it is open. The cancel argument is used as a hint that
-   *  the popup is being closed because it has been cancelled, rather than
-   *  something being selected within the panel.
-   *
-   * @param cancel if true, then the popup is being cancelled.
-   */
-  void hidePopup(optional boolean cancel = false);
-
-  /**
    * Allow the popup to automatically position itself.
    */
   attribute boolean autoPosition;
 
   /**
-   * Size the popup to the given dimensions
-   */
-  void sizeTo(long width, long height);
-
-  /**
-   * Move the popup to a point on screen in CSS pixels.
-   */
-  void moveTo(long left, long top);
-
-  /**
    * Open the popup relative to a specified node at a specific location.
    *
    * The popup may be either anchored to another node or opened freely.
    * To anchor a popup to a node, supply an anchor node and set the position
    * to a string indicating the manner in which the popup should be anchored.
    * Possible values for position are:
    *    before_start, before_end, after_start, after_end,
    *    start_before, start_after, end_before, end_after,
@@ -63,70 +52,91 @@ interface PopupBoxObject : BoxObject
    * be used to move to the left and upwards respectively.
    *
    * Unanchored popups may be created by supplying null as the anchor node.
    * An unanchored popup appears at the position specified by x and y,
    * relative to the viewport of the document containing the popup node. In
    * this case, position and attributesOverride are ignored.
    *
    * @param anchorElement the node to anchor the popup to, may be null
-   * @param position manner is which to anchor the popup to node
+   * @param options either options to use, or a string position
    * @param x horizontal offset
    * @param y vertical offset
    * @param isContextMenu true for context menus, false for other popups
    * @param attributesOverride true if popup node attributes override position
    * @param triggerEvent the event that triggered this popup (mouse click for example)
    */
-  void openPopup(Element? anchorElement,
-                 optional DOMString position = "",
-                 long x,
-                 long y,
-                 boolean isContextMenu,
-                 boolean attributesOverride,
-                 Event? triggerEvent);
+  void openPopup(optional Element? anchorElement = null,
+                 optional StringOrOpenPopupOptions options,
+                 optional long x = 0,
+                 optional long y = 0,
+                 optional boolean isContextMenu = false,
+                 optional boolean attributesOverride = false,
+                 optional Event? triggerEvent = null);
 
   /**
    * Open the popup at a specific screen position specified by x and y. This
    * position may be adjusted if it would cause the popup to be off of the
    * screen. The x and y coordinates are measured in CSS pixels, and like all
    * screen coordinates, are given relative to the top left of the primary
    * screen.
    *
    * @param isContextMenu true for context menus, false for other popups
    * @param x horizontal screen position
    * @param y vertical screen position
    * @param triggerEvent the event that triggered this popup (mouse click for example)
    */
-  void openPopupAtScreen(long x, long y,
-                         boolean isContextMenu,
-                         Event? triggerEvent);
+  void openPopupAtScreen(optional long x = 0, optional long y = 0,
+                         optional boolean isContextMenu = false,
+                         optional Event? triggerEvent = null);
 
   /**
    * Open the popup anchored at a specific screen rectangle. This function is
    * similar to openPopup except that that rectangle of the anchor is supplied
    * rather than an element. The anchor rectangle arguments are screen
    * coordinates.
    */
   void openPopupAtScreenRect(optional DOMString position = "",
-                             long x,
-                             long y,
-                             long width,
-                             long height,
-                             boolean isContextMenu,
-                             boolean attributesOverride,
-                             Event? triggerEvent);
+                             optional long x = 0,
+                             optional long y = 0,
+                             optional long width = 0,
+                             optional long height = 0,
+                             optional boolean isContextMenu = false,
+                             optional boolean attributesOverride = false,
+                             optional Event? triggerEvent = null);
+
+  /**
+   *  Hide the popup if it is open. The cancel argument is used as a hint that
+   *  the popup is being closed because it has been cancelled, rather than
+   *  something being selected within the panel.
+   *
+   * @param cancel if true, then the popup is being cancelled.
+   */
+  void hidePopup(optional boolean cancel = false);
+
+  /**
+   * Attribute getter and setter for label.
+   */
+  [SetterThrows]
+  attribute DOMString label;
+
+  /**
+   * Attribute getter and setter for position.
+   */
+  [SetterThrows]
+  attribute DOMString position;
 
   /**
    * Returns the state of the popup:
    *   closed - the popup is closed
    *   open - the popup is open
    *   showing - the popup is in the process of being shown
    *   hiding - the popup is in the process of being hidden
    */
-  readonly attribute DOMString popupState;
+  readonly attribute DOMString state;
 
   /**
    * The node that triggered the popup. If the popup is not open, will return
    * null.
    */
   readonly attribute Node? triggerNode;
 
   /**
@@ -137,24 +147,34 @@ interface PopupBoxObject : BoxObject
 
   /**
    * Retrieve the screen rectangle of the popup, including the area occupied by
    * any titlebar or borders present.
    */
   DOMRect getOuterScreenRect();
 
   /**
+   * Move the popup to a point on screen in CSS pixels.
+   */
+  void moveTo(long left, long top);
+
+  /**
    * Move an open popup to the given anchor position. The arguments have the same
    * meaning as the corresponding argument to openPopup. This method has no effect
    * on popups that are not open.
    */
-  void moveToAnchor(Element? anchorElement,
+  void moveToAnchor(optional Element? anchorElement = null,
                     optional DOMString position = "",
-                    long x, long y,
-                    boolean attributesOverride);
+                    optional long x = 0, optional long y = 0,
+                    optional boolean attributesOverride = false);
+
+  /**
+   * Size the popup to the given dimensions
+   */
+  void sizeTo(long width, long height);
 
   /** Returns the alignment position where the popup has appeared relative to its
    *  anchor node or point, accounting for any flipping that occurred.
    */
   readonly attribute DOMString alignmentPosition;
   readonly attribute long alignmentOffset;
 
   void setConstraintRect(DOMRectReadOnly rect);
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -716,17 +716,16 @@ WEBIDL_FILES = [
     'PerformanceServerTiming.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'Permissions.webidl',
     'PermissionStatus.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
     'PointerEvent.webidl',
-    'PopupBoxObject.webidl',
     'Position.webidl',
     'PositionError.webidl',
     'Presentation.webidl',
     'PresentationAvailability.webidl',
     'PresentationConnection.webidl',
     'PresentationConnectionList.webidl',
     'PresentationReceiver.webidl',
     'PresentationRequest.webidl',
@@ -948,16 +947,17 @@ WEBIDL_FILES = [
     'XPathEvaluator.webidl',
     'XPathExpression.webidl',
     'XPathNSResolver.webidl',
     'XPathResult.webidl',
     'XSLTProcessor.webidl',
     'XULCommandEvent.webidl',
     'XULDocument.webidl',
     'XULElement.webidl',
+    'XULPopupElement.webidl',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     WEBIDL_FILES += [
         'PeerConnectionImpl.webidl',
         'PeerConnectionImplEnums.webidl',
         'PeerConnectionObserver.webidl',
         'PeerConnectionObserverEnums.webidl',
--- a/dom/websocket/tests/test_websocket_frame.html
+++ b/dom/websocket/tests/test_websocket_frame.html
@@ -31,17 +31,17 @@ var innerId =
         .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 ok(innerId, "We have a valid innerWindowID: " + innerId);
 
 var service = Cc["@mozilla.org/websocketevent/service;1"]
                 .getService(Ci.nsIWebSocketEventService);
 ok(!!service, "We have the nsIWebSocketEventService");
 
 var listener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebSocketEventListener]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebSocketEventListener]),
 
   webSocketCreated: function(aWebSocketSerialID, aURI, aProtocols) {
     info("WebSocketCreated");
 
     is(aURI, URI, "URI matches");
     is(aProtocols, "frame", "Protocol matches");
 
     webSocketCreatedCounter++;
--- a/dom/workers/test/extensions/bootstrap/bootstrap.js
+++ b/dom/workers/test/extensions/bootstrap/bootstrap.js
@@ -91,17 +91,17 @@ WorkerTestBootstrap.prototype = {
         gWorkerAndCallback.terminate();
         break;
 
       default:
         throw new Error("Unknown worker command");
     }
   },
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver])
 };
 
 var gFactory = {
   register: function() {
     var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 
     var classID = Components.ID("{36b5df0b-8dcf-4aa2-9c45-c51d871295f9}");
     var description = "WorkerTestBootstrap";
--- a/dom/workers/test/test_extensionBootstrap.xul
+++ b/dom/workers/test/test_extensionBootstrap.xul
@@ -36,17 +36,17 @@
                                             "." + symbol),
                  true,
                  "Symbol '" + symbol + "' present during '" + stage + "'");
             }
           }
 
           SimpleTest.finish();
         },
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+        QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver])
       };
 
       var workertestbootstrap = Cc["@mozilla.org/test/workertestbootstrap;1"].
                                 createInstance(Ci.nsIObserver);
 
       workertestbootstrap.observe(observer, "postMessage", message);
 
       SimpleTest.waitForExplicitFinish();
--- a/dom/xbl/nsXBLContentSink.cpp
+++ b/dom/xbl/nsXBLContentSink.cpp
@@ -869,17 +869,17 @@ nsXBLContentSink::CreateElement(const ch
   *aAppendContent = true;
   RefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
 
   prototype->mNodeInfo = aNodeInfo;
 
   AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
 
   Element* result;
-  nsresult rv = nsXULElement::Create(prototype, mDocument, false, false, &result);
+  nsresult rv = nsXULElement::CreateFromPrototype(prototype, mDocument, false, false, &result);
   *aResult = result;
   return rv;
 #endif
 }
 
 nsresult
 nsXBLContentSink::AddAttributes(const char16_t** aAtts, Element* aElement)
 {
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -1295,17 +1295,17 @@ nsXBLPrototypeBinding::ReadContentNode(n
         attrs[i].mName.SetTo(ni);
       }
 
       rv = prototype->SetAttrAt(i, val, documentURI);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsresult rv =
-      nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(element));
+      nsXULElement::CreateFromPrototype(prototype, aDocument, false, false, getter_AddRefs(element));
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
 #endif
     NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER);
 
     for (uint32_t i = 0; i < attrCount; i++) {
       rv = ReadNamespace(aStream, namespaceID);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/xslt/xslt/txEXSLTRegExFunctions.js
+++ b/dom/xslt/xslt/txEXSLTRegExFunctions.js
@@ -11,17 +11,17 @@ function txEXSLTRegExFunctions()
 {
 }
 
 var SingletonInstance = null;
 
 txEXSLTRegExFunctions.prototype = {
     classID: EXSLT_REGEXP_CID,
 
-    QueryInterface: XPCOMUtils.generateQI([Ci.txIEXSLTRegExFunctions]),
+    QueryInterface: ChromeUtils.generateQI([Ci.txIEXSLTRegExFunctions]),
 
     // txIEXSLTRegExFunctions
     match: function(str, regex, flags, doc) {
         var docFrag = doc.createDocumentFragment();
         var re = new RegExp(regex, flags);
         var matches = str.match(re);
         if (matches != null) {
             for (var i = 0; i < matches.length; ++i) {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3319,17 +3319,17 @@ XULDocument::CreateElementFromPrototype(
                 NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
     }
 
     RefPtr<Element> result;
 
     if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
         // If it's a XUL element, it'll be lightweight until somebody
         // monkeys with it.
-        rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
+        rv = nsXULElement::CreateFromPrototype(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
         if (NS_FAILED(rv)) return rv;
     }
     else {
         // If it's not a XUL element, it's gonna be heavyweight no matter
         // what. So we need to copy everything out of the prototype
         // into the element.  Get a nodeinfo from our nodeinfo manager
         // for this node.
         RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
rename from layout/xul/PopupBoxObject.cpp
rename to dom/xul/XULPopupElement.cpp
--- a/layout/xul/PopupBoxObject.cpp
+++ b/dom/xul/XULPopupElement.cpp
@@ -9,195 +9,197 @@
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsNameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsMenuPopupFrame.h"
 #include "nsView.h"
 #include "mozilla/AppUnits.h"
 #include "mozilla/dom/DOMRect.h"
-#include "mozilla/dom/PopupBoxObject.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
-#include "mozilla/dom/PopupBoxObjectBinding.h"
+#include "mozilla/dom/XULPopupElement.h"
+#include "mozilla/dom/XULPopupElementBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-PopupBoxObject::PopupBoxObject()
+nsXULElement*
+NS_NewXULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
+  return new XULPopupElement(aNodeInfo);
 }
 
-PopupBoxObject::~PopupBoxObject()
+JSObject*
+XULPopupElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
-}
-
-nsIContent* PopupBoxObject::GetParentObject() const
-{
-  return BoxObject::GetParentObject();
+  return XULPopupElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
-JSObject* PopupBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+nsIFrame*
+XULPopupElement::GetFrame(bool aFlushLayout)
 {
-  return PopupBoxObjectBinding::Wrap(aCx, this, aGivenProto);
-}
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
 
-nsPopupSetFrame*
-PopupBoxObject::GetPopupSetFrame()
-{
-  nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(false));
-  if (!rootBox)
-    return nullptr;
+  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(aFlushLayout ? FlushType::Layout : FlushType::Frames);
+  }
 
-  return rootBox->GetPopupSetFrame();
+  return GetPrimaryFrame();
 }
 
 void
-PopupBoxObject::HidePopup(bool aCancel)
+XULPopupElement::OpenPopup(Element* aAnchorElement,
+                           const StringOrOpenPopupOptions& aOptions,
+                           int32_t aXPos, int32_t aYPos,
+                           bool aIsContextMenu,
+                           bool aAttributesOverride,
+                           Event* aTriggerEvent)
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    pm->HidePopup(mContent, false, true, false, aCancel);
+  nsAutoString position;
+  if (aOptions.IsOpenPopupOptions()) {
+    const OpenPopupOptions& options = aOptions.GetAsOpenPopupOptions();
+    position = options.mPosition;
+    aXPos = options.mX;
+    aYPos = options.mY;
+    aIsContextMenu = options.mIsContextMenu;
+    aAttributesOverride = options.mAttributesOverride;
+    aTriggerEvent = options.mTriggerEvent;
   }
-}
+  else {
+    position = aOptions.GetAsString();
+  }
 
-void
-PopupBoxObject::ShowPopup(Element* aAnchorElement,
-                          Element& aPopupElement,
-                          int32_t aXPos, int32_t aYPos,
-                          const nsAString& aPopupType,
-                          const nsAString& aAnchorAlignment,
-                          const nsAString& aPopupAlignment)
-{
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    nsAutoString popupType(aPopupType);
-    nsAutoString anchor(aAnchorAlignment);
-    nsAutoString align(aPopupAlignment);
-    pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align,
-                                 aXPos, aYPos,
-                                 popupType.EqualsLiteral("context"));
-  }
-}
+  if (pm) {
+    // As a special case for popups that are menus when no anchor or position are
+    // specified, open the popup with ShowMenu instead of ShowPopup so that the
+    // popup is aligned with the menu.
+    if (!aAnchorElement && position.IsEmpty() && GetPrimaryFrame()) {
+      nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame()->GetParent());
+      if (menu) {
+        pm->ShowMenu(menu->GetContent(), false, false);
+        return;
+      }
+    }
 
-void
-PopupBoxObject::OpenPopup(Element* aAnchorElement,
-                          const nsAString& aPosition,
-                          int32_t aXPos, int32_t aYPos,
-                          bool aIsContextMenu,
-                          bool aAttributesOverride,
-                          Event* aTriggerEvent)
-{
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos,
+    pm->ShowPopup(this, aAnchorElement, position, aXPos, aYPos,
                   aIsContextMenu, aAttributesOverride, false, aTriggerEvent);
   }
 }
 
 void
-PopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
-                                  bool aIsContextMenu,
-                                  Event* aTriggerEvent)
+XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
+                                   bool aIsContextMenu,
+                                   Event* aTriggerEvent)
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent)
-    pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
+  if (pm) {
+    pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
+  }
 }
 
 void
-PopupBoxObject::OpenPopupAtScreenRect(const nsAString& aPosition,
-                                      int32_t aXPos, int32_t aYPos,
-                                      int32_t aWidth, int32_t aHeight,
-                                      bool aIsContextMenu,
-                                      bool aAttributesOverride,
-                                      Event* aTriggerEvent)
+XULPopupElement::OpenPopupAtScreenRect(const nsAString& aPosition,
+                                       int32_t aXPos, int32_t aYPos,
+                                       int32_t aWidth, int32_t aHeight,
+                                       bool aIsContextMenu,
+                                       bool aAttributesOverride,
+                                       Event* aTriggerEvent)
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    pm->ShowPopupAtScreenRect(mContent, aPosition,
+  if (pm) {
+    pm->ShowPopupAtScreenRect(this, aPosition,
                               nsIntRect(aXPos, aYPos, aWidth, aHeight),
                               aIsContextMenu, aAttributesOverride, aTriggerEvent);
   }
 }
 
 void
-PopupBoxObject::MoveTo(int32_t aLeft, int32_t aTop)
+XULPopupElement::HidePopup(bool aCancel)
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+  if (pm) {
+    pm->HidePopup(this, false, true, false, aCancel);
+  }
+}
+
+void
+XULPopupElement::MoveTo(int32_t aLeft, int32_t aTop)
+{
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     menuPopupFrame->MoveTo(CSSIntPoint(aLeft, aTop), true);
   }
 }
 
 void
-PopupBoxObject::MoveToAnchor(Element* aAnchorElement,
-                             const nsAString& aPosition,
-                             int32_t aXPos, int32_t aYPos,
-                             bool aAttributesOverride)
+XULPopupElement::MoveToAnchor(Element* aAnchorElement,
+                              const nsAString& aPosition,
+                              int32_t aXPos, int32_t aYPos,
+                              bool aAttributesOverride)
 {
-  if (mContent) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-
-    nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-    if (menuPopupFrame && menuPopupFrame->IsVisible()) {
-      menuPopupFrame->MoveToAnchor(anchorContent, aPosition, aXPos, aYPos, aAttributesOverride);
-    }
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
+  if (menuPopupFrame && menuPopupFrame->IsVisible()) {
+    menuPopupFrame->MoveToAnchor(aAnchorElement, aPosition, aXPos, aYPos, aAttributesOverride);
   }
 }
 
 void
-PopupBoxObject::SizeTo(int32_t aWidth, int32_t aHeight)
+XULPopupElement::SizeTo(int32_t aWidth, int32_t aHeight)
 {
-  if (!mContent)
-    return;
-
   nsAutoString width, height;
   width.AppendInt(aWidth);
   height.AppendInt(aHeight);
 
-  RefPtr<Element> element = mContent->AsElement();
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
 
   // We only want to pass aNotify=true to SetAttr once, but must make sure
   // we pass it when a value is being changed.  Thus, we check if the height
   // is the same and if so, pass true when setting the width.
-  bool heightSame = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters);
+  bool heightSame = AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters);
+
+  SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame);
+  SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true);
 
-  element->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame);
-  element->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true);
+  // If the popup is open, force a reposition of the popup after resizing it
+  // with notifications set to true so that the popuppositioned event is fired.
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
+  if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupShown) {
+    menuPopupFrame->SetPopupPosition(nullptr, false, false, true);
+  }
 }
 
 bool
-PopupBoxObject::AutoPosition()
+XULPopupElement::AutoPosition()
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     return menuPopupFrame->GetAutoPosition();
   }
   return true;
 }
 
 void
-PopupBoxObject::SetAutoPosition(bool aShouldAutoPosition)
+XULPopupElement::SetAutoPosition(bool aShouldAutoPosition)
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     menuPopupFrame->SetAutoPosition(aShouldAutoPosition);
   }
 }
 
 void
-PopupBoxObject::GetPopupState(nsString& aState)
+XULPopupElement::GetState(nsString& aState)
 {
   // set this here in case there's no frame for the popup
   aState.AssignLiteral("closed");
 
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     switch (menuPopupFrame->PopupState()) {
       case ePopupShown:
         aState.AssignLiteral("open");
         break;
       case ePopupShowing:
       case ePopupPositioning:
       case ePopupOpening:
@@ -213,38 +215,38 @@ PopupBoxObject::GetPopupState(nsString& 
       default:
         NS_NOTREACHED("Bad popup state");
         break;
     }
   }
 }
 
 nsINode*
-PopupBoxObject::GetTriggerNode() const
+XULPopupElement::GetTriggerNode() const
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   return nsMenuPopupFrame::GetTriggerContent(menuPopupFrame);
 }
 
 Element*
-PopupBoxObject::GetAnchorNode() const
+XULPopupElement::GetAnchorNode() const
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (!menuPopupFrame) {
     return nullptr;
   }
 
   nsIContent* anchor = menuPopupFrame->GetAnchor();
   return anchor && anchor->IsElement() ? anchor->AsElement() : nullptr;
 }
 
 already_AddRefed<DOMRect>
-PopupBoxObject::GetOuterScreenRect()
+XULPopupElement::GetOuterScreenRect()
 {
-  RefPtr<DOMRect> rect = new DOMRect(mContent);
+  RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
 
   // Return an empty rectangle if the popup is not open.
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (!menuPopupFrame || !menuPopupFrame->IsOpen()) {
     return rect.forget();
   }
 
   nsView* view = menuPopupFrame->GetView();
@@ -256,17 +258,17 @@ PopupBoxObject::GetOuterScreenRect()
       int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel();
       rect->SetLayoutRect(LayoutDeviceIntRect::ToAppUnits(screenRect, pp));
     }
   }
   return rect.forget();
 }
 
 void
-PopupBoxObject::GetAlignmentPosition(nsString& positionStr)
+XULPopupElement::GetAlignmentPosition(nsString& positionStr)
 {
   positionStr.Truncate();
 
   // This needs to flush layout.
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true));
   if (!menuPopupFrame)
     return;
 
@@ -307,45 +309,35 @@ PopupBoxObject::GetAlignmentPosition(nsS
       break;
     default:
       // Leave as an empty string.
       break;
   }
 }
 
 int32_t
-PopupBoxObject::AlignmentOffset()
+XULPopupElement::AlignmentOffset()
 {
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (!menuPopupFrame)
     return 0;
 
   int32_t pp = mozilla::AppUnitsPerCSSPixel();
   // Note that the offset might be along either the X or Y axis, but for the
   // sake of simplicity we use a point with only the X axis set so we can
   // use ToNearestPixels().
   nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0);
   nsIntPoint popupOffset = appOffset.ToNearestPixels(pp);
   return popupOffset.x;
 }
 
 void
-PopupBoxObject::SetConstraintRect(dom::DOMRectReadOnly& aRect)
+XULPopupElement::SetConstraintRect(dom::DOMRectReadOnly& aRect)
 {
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (menuPopupFrame) {
     menuPopupFrame->SetOverrideConstraintRect(
       LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
   }
 }
 
 } // namespace dom
 } // namespace mozilla
-
-// Creation Routine ///////////////////////////////////////////////////////////////////////
-
-nsresult
-NS_NewPopupBoxObject(nsIBoxObject** aResult)
-{
-  *aResult = new mozilla::dom::PopupBoxObject();
-  NS_ADDREF(*aResult);
-  return NS_OK;
-}
rename from layout/xul/PopupBoxObject.h
rename to dom/xul/XULPopupElement.h
--- a/layout/xul/PopupBoxObject.h
+++ b/dom/xul/XULPopupElement.h
@@ -1,64 +1,72 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 
-#ifndef mozilla_dom_PopupBoxObject_h
-#define mozilla_dom_PopupBoxObject_h
+#ifndef XULPopupElement_h__
+#define XULPopupElement_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "mozilla/dom/BoxObject.h"
 #include "nsString.h"
+#include "nsXULElement.h"
 
 struct JSContext;
-class nsPopupSetFrame;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRect;
 class Element;
 class Event;
+class StringOrOpenPopupOptions;
 
-class PopupBoxObject final : public BoxObject
+nsXULElement*
+NS_NewXULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
+
+class XULPopupElement final : public nsXULElement
 {
+private:
+  nsIFrame* GetFrame(bool aFlushLayout);
+
 public:
-  NS_INLINE_DECL_REFCOUNTING_INHERITED(PopupBoxObject, BoxObject)
-
-  PopupBoxObject();
+  explicit XULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+    : nsXULElement(aNodeInfo)
+  {
+  }
 
-  nsIContent* GetParentObject() const;
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  void GetLabel(DOMString& aValue) const
+  {
+      GetXULAttr(nsGkAtoms::label, aValue);
+  }
+  void SetLabel(const nsAString& aValue, ErrorResult& rv)
+  {
+      SetXULAttr(nsGkAtoms::label, aValue, rv);
+  }
 
-  void ShowPopup(Element* aAnchorElement,
-                 Element& aPopupElement,
-                 int32_t aXPos,
-                 int32_t aYPos,
-                 const nsAString& aPopupType,
-                 const nsAString& aAnchorAlignment,
-                 const nsAString& aPopupAlignment);
-
-  void HidePopup(bool aCancel);
+  void GetPosition(DOMString& aValue) const
+  {
+      GetXULAttr(nsGkAtoms::position, aValue);
+  }
+  void SetPosition(const nsAString& aValue, ErrorResult& rv)
+  {
+      SetXULAttr(nsGkAtoms::position, aValue, rv);
+  }
 
   bool AutoPosition();
 
   void SetAutoPosition(bool aShouldAutoPosition);
 
-  void SizeTo(int32_t aWidth, int32_t aHeight);
-
-  void MoveTo(int32_t aLeft, int32_t aTop);
-
   void OpenPopup(Element* aAnchorElement,
-                 const nsAString& aPosition,
+                 const StringOrOpenPopupOptions& aOptions,
                  int32_t aXPos,
                  int32_t aYPos,
                  bool aIsContextMenu, bool aAttributesOverride,
                  Event* aTriggerEvent);
 
   void OpenPopupAtScreen(int32_t aXPos,
                          int32_t aYPos,
                          bool aIsContextMenu,
@@ -66,39 +74,46 @@ public:
 
   void OpenPopupAtScreenRect(const nsAString& aPosition,
                              int32_t aXPos, int32_t aYPos,
                              int32_t aWidth, int32_t aHeight,
                              bool aIsContextMenu,
                              bool aAttributesOverride,
                              Event* aTriggerEvent);
 
-  void GetPopupState(nsString& aState);
+  void HidePopup(bool aCancel);
+
+  void GetState(nsString& aState);
 
   nsINode* GetTriggerNode() const;
 
   Element* GetAnchorNode() const;
 
   already_AddRefed<DOMRect> GetOuterScreenRect();
 
+  void MoveTo(int32_t aLeft, int32_t aTop);
+
   void MoveToAnchor(Element* aAnchorElement,
                     const nsAString& aPosition,
                     int32_t aXPos,
                     int32_t aYPos,
                     bool aAttributesOverride);
 
+  void SizeTo(int32_t aWidth, int32_t aHeight);
+
   void GetAlignmentPosition(nsString& positionStr);
 
   int32_t AlignmentOffset();
 
-  void SetConstraintRect(dom::DOMRectReadOnly& aRect);
-
-private:
-  ~PopupBoxObject();
+  void SetConstraintRect(DOMRectReadOnly& aRect);
 
 protected:
-  nsPopupSetFrame* GetPopupSetFrame();
+  virtual ~XULPopupElement()
+  {
+  }
+
+  JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_PopupBoxObject_h
+#endif // XULPopupElement_h
--- a/dom/xul/crashtests/384877-1-inner.xul
+++ b/dom/xul/crashtests/384877-1-inner.xul
@@ -2,14 +2,11 @@
 <script>
 function doe() {
 var d = document.getElementById('d');
 if (d.openPopup) {
   d.openPopup(document.documentElement, 'before_start', 0, 0, false, false);
 //  alert(d.state);
 }
   
-if (d.showPopup)
-  d.showPopup();
-}
 setTimeout(doe, 200);
 </script>
 </menupopup>
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -18,26 +18,31 @@ if CONFIG['MOZ_XUL']:
     XPIDL_SOURCES += [
         'nsIXULOverlayProvider.idl',
     ]
 
     EXPORTS += [
         'nsXULElement.h',
     ]
 
+    EXPORTS.mozilla.dom += [
+        'XULPopupElement.h',
+    ]
+
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULContentUtils.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
         'nsXULPrototypeCache.cpp',
         'nsXULPrototypeDocument.cpp',
         'nsXULSortService.cpp',
         'XULDocument.cpp',
+        'XULPopupElement.cpp',
     ]
 
 XPIDL_SOURCES += [
     'nsIController.idl',
     'nsIControllers.idl',
     'nsIXULSortService.idl',
 ]
 
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -70,16 +70,17 @@
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
 #include "nsXULTooltipListener.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsICSSDeclaration.h"
 #include "nsLayoutUtils.h"
+#include "XULPopupElement.h"
 
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/BoxObject.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/dom/XULCommandEvent.h"
 
@@ -123,17 +124,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULEl
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULElementTearoff)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoaderOwner)
 NS_INTERFACE_MAP_END_AGGREGATED(mElement)
 
 //----------------------------------------------------------------------
 // nsXULElement
 //
 
-nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo)
+nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : nsStyledElement(aNodeInfo),
       mBindingParent(nullptr)
 {
     XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
 
     // We may be READWRITE by default; check.
     if (IsReadWriteTextElement()) {
         AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
@@ -157,19 +158,41 @@ nsXULElement::MaybeUpdatePrivateLifetime
     nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
     nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr;
     if (docShell) {
         docShell->SetAffectPrivateSessionLifetime(false);
     }
 }
 
 /* static */
+nsXULElement* NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
+{
+  return new nsXULElement(aNodeInfo);
+}
+
+ /* static */
+nsXULElement* nsXULElement::Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
+{
+  RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
+  if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
+      nodeInfo->Equals(nsGkAtoms::popup) ||
+      nodeInfo->Equals(nsGkAtoms::panel) ||
+      nodeInfo->Equals(nsGkAtoms::tooltip)) {
+    return NS_NewXULPopupElement(nodeInfo.forget());
+  }
+
+  return NS_NewBasicXULElement(nodeInfo.forget());
+}
+
+/* static */
 already_AddRefed<nsXULElement>
-nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo,
-                     bool aIsScriptable, bool aIsRoot)
+nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
+                                  mozilla::dom::NodeInfo *aNodeInfo,
+                                  bool aIsScriptable,
+                                  bool aIsRoot)
 {
     RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     nsCOMPtr<Element> baseElement;
     NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(), dom::FROM_PARSER_NETWORK);
 
     if (baseElement) {
         nsXULElement* element = FromNode(baseElement);
 
@@ -204,21 +227,21 @@ nsXULElement::Create(nsXULPrototypeEleme
 
         return baseElement.forget().downcast<nsXULElement>();
     }
 
     return nullptr;
 }
 
 nsresult
-nsXULElement::Create(nsXULPrototypeElement* aPrototype,
-                     nsIDocument* aDocument,
-                     bool aIsScriptable,
-                     bool aIsRoot,
-                     Element** aResult)
+nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
+                                  nsIDocument* aDocument,
+                                  bool aIsScriptable,
+                                  bool aIsRoot,
+                                  Element** aResult)
 {
     // Create an nsXULElement from a prototype
     NS_PRECONDITION(aPrototype != nullptr, "null ptr");
     if (! aPrototype)
         return NS_ERROR_NULL_POINTER;
 
     NS_PRECONDITION(aResult != nullptr, "null ptr");
     if (! aResult)
@@ -229,18 +252,18 @@ nsXULElement::Create(nsXULPrototypeEleme
         mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo;
         nodeInfo = aDocument->NodeInfoManager()->
           GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(),
                       ELEMENT_NODE);
     } else {
         nodeInfo = aPrototype->mNodeInfo;
     }
 
-    RefPtr<nsXULElement> element = Create(aPrototype, nodeInfo,
-                                            aIsScriptable, aIsRoot);
+    RefPtr<nsXULElement> element = CreateFromPrototype(aPrototype, nodeInfo,
+                                                       aIsScriptable, aIsRoot);
     element.forget(aResult);
 
     return NS_OK;
 }
 
 nsresult
 NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                  FromParser aFromParser)
@@ -263,17 +286,17 @@ NS_NewXULElement(Element** aResult, alre
 void
 NS_TrustedNewXULElement(Element** aResult,
                         already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
     RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     NS_PRECONDITION(ni, "need nodeinfo for non-proto Create");
 
     // Create an nsXULElement with the specified namespace and tag.
-    NS_ADDREF(*aResult = new nsXULElement(ni.forget()));
+    NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget()));
 }
 
 //----------------------------------------------------------------------
 // nsISupports interface
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
@@ -301,17 +324,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsStyled
 
 nsresult
 nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                     bool aPreallocateChildren) const
 {
     *aResult = nullptr;
 
     RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
-    RefPtr<nsXULElement> element = new nsXULElement(ni.forget());
+    RefPtr<nsXULElement> element = Construct(ni.forget());
 
     nsresult rv = element->mAttrsAndChildren.EnsureCapacityToClone(mAttrsAndChildren,
                                                                    aPreallocateChildren);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Note that we're _not_ copying mControllers.
 
     uint32_t count = mAttrsAndChildren.AttrCount();
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -327,27 +327,36 @@ enum {
   XUL_ELEMENT_HAS_CONTENTMENU_LISTENER =  XUL_ELEMENT_FLAG_BIT(0),
   XUL_ELEMENT_HAS_POPUP_LISTENER =        XUL_ELEMENT_FLAG_BIT(1)
 };
 
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
 
 #undef XUL_ELEMENT_FLAG_BIT
 
-class nsXULElement final : public nsStyledElement,
-                           public nsIDOMNode
+class nsXULElement : public nsStyledElement,
+                     public nsIDOMNode
 {
+protected:
+    // Use Construct to construct elements instead of this constructor.
+    explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+
 public:
     using Element::Blur;
     using Element::Focus;
-    explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo);
 
     static nsresult
-    Create(nsXULPrototypeElement* aPrototype, nsIDocument* aDocument,
-           bool aIsScriptable, bool aIsRoot, mozilla::dom::Element** aResult);
+    CreateFromPrototype(nsXULPrototypeElement* aPrototype,
+                        nsIDocument* aDocument,
+                        bool aIsScriptable,
+                        bool aIsRoot,
+                        mozilla::dom::Element** aResult);
+
+    // This is the constructor for nsXULElements.
+    static nsXULElement* Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
     NS_IMPL_FROMNODE(nsXULElement, kNameSpaceID_XUL)
 
     // nsISupports
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement)
 
     // nsINode
@@ -718,25 +727,30 @@ protected:
     nsIControllers *Controllers() {
       nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
       return slots ? slots->mControllers.get() : nullptr;
     }
 
     void UnregisterAccessKey(const nsAString& aOldValue);
     bool BoolAttrIsTrue(nsAtom* aName) const;
 
+    friend nsXULElement*
+    NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
+
     friend nsresult
     NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo,
                      mozilla::dom::FromParser aFromParser, const nsAString* aIs);
     friend void
     NS_TrustedNewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo);
 
     static already_AddRefed<nsXULElement>
-    Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo,
-           bool aIsScriptable, bool aIsRoot);
+    CreateFromPrototype(nsXULPrototypeElement* aPrototype,
+                        mozilla::dom::NodeInfo *aNodeInfo,
+                        bool aIsScriptable,
+                        bool aIsRoot);
 
     bool IsReadWriteTextElement() const
     {
         return IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::textarea) &&
                !HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
     }
 
     virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/editor/composer/test/test_bug1266815.html
+++ b/editor/composer/test/test_bug1266815.html
@@ -33,17 +33,17 @@ var helperAppDlgPromise = new Promise(fu
 
   HelperAppLauncherDialog.prototype = {
     show: function(aLauncher, aWindowContext, aReason) {
       ok(true, "Whether showing Dialog");
       resolve();
       registrar.unregisterFactory(MOCK_HELPERAPP_DIALOG_CID,
                                   mockHelperAppService);
     },
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
   };
 
   mockHelperAppService = XPCOMUtils._getFactory(HelperAppLauncherDialog);
   registrar.registerFactory(MOCK_HELPERAPP_DIALOG_CID, "",
                             HELPERAPP_DIALOG_CONTRACT_ID,
                             mockHelperAppService);
 });
 
--- a/editor/libeditor/EditorUtils.js
+++ b/editor/libeditor/EditorUtils.js
@@ -9,17 +9,17 @@ ChromeUtils.import("resource://gre/modul
 
 const EDITORUTILS_CID = Components.ID('{12e63991-86ac-4dff-bb1a-703495d67d17}');
 
 function EditorUtils() {
 }
 
 EditorUtils.prototype = {
   classID: EDITORUTILS_CID,
-  QueryInterface: XPCOMUtils.generateQI([ Ci.nsIEditorUtils ]),
+  QueryInterface: ChromeUtils.generateQI([ Ci.nsIEditorUtils ]),
 
   slurpBlob(aBlob, aScope, aListener) {
     let reader = new aScope.FileReader();
     reader.addEventListener("load", (event) => {
       aListener.onResult(event.target.result);
     });
     reader.addEventListener("error", (event) => {
       aListener.onError(event.target.error.message);
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -10,17 +10,17 @@ var PERMISSIONS_FILE_NAME = "permissions
 var CONTRACT_ID = "@mozilla.org/browser/nav-history-service;1";
 var factory = {
   createInstance: function() {
     throw new Error("There is no history service");
   },
   lockFactory: function() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory])
 };
 
 var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
 
 var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 var oldClassID = registrar.contractIDToCID(CONTRACT_ID);
 var oldFactory = Components.manager.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
 registrar.unregisterFactory(oldClassID, oldFactory);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -303,17 +303,16 @@ public:
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mRenderMode(RENDER_GDI)
-  , mUsingDirectWrite(false)
 {
   /*
    * Initialize COM
    */
   CoInitialize(nullptr);
 
   RegisterStrongMemoryReporter(new GfxD2DVramReporter());
   RegisterStrongMemoryReporter(new GPUAdapterReporter());
@@ -557,28 +556,30 @@ gfxWindowsPlatform::CreatePlatformFontLi
 {
     gfxPlatformFontList *pfl;
 
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
     // crashers so blacklist them altogether
     if (IsNotWin7PreRTM() && DWriteEnabled()) {
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
-            mUsingDirectWrite = true;
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
         DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
                    NS_LITERAL_CSTRING("FEATURE_FAILURE_FONT_FAIL"));
     }
 
-    mUsingDirectWrite = false;
+    // Ensure this is false, even if the Windows version was recent enough to
+    // permit it, as we're using GDI fonts.
+    mHasVariationFontSupport = false;
+
     pfl = new gfxGDIFontList();
 
     if (NS_SUCCEEDED(pfl->InitFontList())) {
         return pfl;
     }
 
     gfxPlatformFontList::Shutdown();
     return nullptr;
@@ -2065,11 +2066,11 @@ gfxWindowsPlatform::SupportsPluginDirect
     return false;
   }
   return true;
 }
 
 bool
 gfxWindowsPlatform::CheckVariationFontSupport()
 {
-  // Variation font support is only on Fall Creators Update or later
-  return mUsingDirectWrite && IsWin10FallCreatorsUpdateOrLater();
+  // Variation font support is only available on Fall Creators Update or later.
+  return IsWin10FallCreatorsUpdateOrLater();
 }
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -262,13 +262,11 @@ private:
     void InitializeAdvancedLayersConfig();
 
     RefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 
     RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
 
     nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
-
-    bool mUsingDirectWrite;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/intl/l10n/Localization.jsm
+++ b/intl/l10n/Localization.jsm
@@ -16,17 +16,16 @@
  */
 
 
 /* fluent-dom@0.2.0 */
 
 /* eslint no-console: ["error", { allow: ["warn", "error"] }] */
 /* global console */
 
-const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
 const { L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
 
 /*
  * CachedAsyncIterable caches the elements yielded by an iterable.
  *
  * It can be used to iterate over an iterable many times without depleting the
@@ -272,17 +271,17 @@ class Localization {
    * This method should be called when there's a reason to believe
    * that language negotiation or available resources changed.
    */
   onLanguageChange() {
     this.ctxs = new CachedAsyncIterable(this.generateMessages(this.resourceIds));
   }
 }
 
-Localization.prototype.QueryInterface = XPCOMUtils.generateQI([
+Localization.prototype.QueryInterface = ChromeUtils.generateQI([
   Ci.nsISupportsWeakReference
 ]);
 
 /**
  * Format the value of a message into a string.
  *
  * This function is passed as a method to `keysFromContext` and resolve
  * a value of a single L10n Entity using provided `MessageContext`.
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BCEParserHandle.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef frontend_BCEParserHandle_h
+#define frontend_BCEParserHandle_h
+
+#include "frontend/ErrorReporter.h"
+#include "frontend/FullParseHandler.h"
+#include "frontend/Parser.h"
+
+namespace js {
+namespace frontend {
+
+struct BCEParserHandle {
+    virtual ErrorReporter& errorReporter() = 0;
+    virtual const ErrorReporter& errorReporter() const = 0;
+
+    virtual const JS::ReadOnlyCompileOptions& options() = 0;
+
+    virtual FullParseHandler& astGenerator() = 0;
+    virtual ObjectBox* newObjectBox(JSObject *obj) = 0;
+};
+
+} // namespace frontend
+} // namespace js
+
+#endif // frontend_BCEParserHandle_h
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -370,19 +370,51 @@ BinASTParser<Tok>::poison()
 
 template<typename Tok> void
 BinASTParser<Tok>::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
 {
     ErrorMetadata metadata;
     metadata.filename = getFilename();
     metadata.lineNumber = 0;
     metadata.columnNumber = offset();
+    metadata.isMuted = options().mutedErrors();
     ReportCompileError(cx_, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 }
 
+template<typename Tok> void
+BinASTParser<Tok>::errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args)
+{
+    ErrorMetadata metadata;
+    metadata.filename = getFilename();
+    metadata.lineNumber = 0;
+    metadata.columnNumber = offset;
+    metadata.isMuted = options().mutedErrors();
+    ReportCompileError(cx_, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, *args);
+}
+
+template<typename Tok> bool
+BinASTParser<Tok>::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber, va_list* args)
+{
+    if (!options().extraWarningsOption)
+        return true;
+
+    ErrorMetadata metadata;
+    metadata.filename = getFilename();
+    metadata.lineNumber = 0;
+    metadata.columnNumber = offset;
+    metadata.isMuted = options().mutedErrors();
+
+    if (options().werrorOption) {
+        ReportCompileError(cx_, Move(metadata), Move(notes), JSREPORT_STRICT, errorNumber, *args);
+        return false;
+    }
+
+    return ReportCompileWarning(cx_, Move(metadata), Move(notes), JSREPORT_STRICT | JSREPORT_WARNING, errorNumber, *args);
+}
+
 bool
 BinASTParserBase::hasUsedName(HandlePropertyName name)
 {
     if (UsedNamePtr p = usedNames_.lookup(name))
         return p->value().isUsedInScript(parseContext_->scriptId());
 
     return false;
 }
--- a/js/src/frontend/BinSource.h
+++ b/js/src/frontend/BinSource.h
@@ -224,33 +224,49 @@ class BinASTParser : public BinASTParser
   private: // Implement ErrorReporter
     const ReadOnlyCompileOptions& options_;
 
     const ReadOnlyCompileOptions& options() const override {
         return this->options_;
     }
 
     virtual void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const override {
-        *line = 0;
-        *column = offset;
+        *line = lineAt(offset);
+        *column = columnAt(offset);
+    }
+    virtual uint32_t lineAt(size_t offset) const override {
+        return 0;
+    }
+    virtual uint32_t columnAt(size_t offset) const override {
+        return offset;
     }
+
+    virtual bool isOnThisLine(size_t offset, uint32_t lineNum, bool *isOnSameLine) const override {
+        if (lineNum != 0)
+            return false;
+        *isOnSameLine = true;
+        return true;
+    }
+
     virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const override {
         *line = 0;
         *column = offset();
     }
     size_t offset() const {
         if (tokenizer_.isSome())
             return tokenizer_->offset();
 
         return 0;
     }
     virtual bool hasTokenizationStarted() const override {
         return tokenizer_.isSome();
     }
     virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) override;
+    virtual void errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args) override;
+    virtual bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber, va_list* args) override;
     virtual const char* getFilename() const override {
         return this->options_.filename();
     }
 
     Maybe<Tokenizer> tokenizer_;
     VariableDeclarationKind variableDeclarationKind_;
 
     friend class BinParseContext;
@@ -289,9 +305,9 @@ class BinParseContext : public ParseCont
 
 
 extern template class BinASTParser<BinTokenReaderMultipart>;
 extern template class BinASTParser<BinTokenReaderTester>;
 
 } // namespace frontend
 } // namespace js
 
-#endif // frontend_BinSource_h
\ No newline at end of file
+#endif // frontend_BinSource_h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -20,17 +20,16 @@
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Nestable.h"
 #include "frontend/Parser.h"
-#include "frontend/TokenStream.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/Stack.h"
@@ -2136,29 +2135,29 @@ class ForOfLoopControl : public LoopCont
             if (!bce->emit1(JSOP_POP))                    //
                 return false;
         }
 
         return true;
     }
 };
 
+
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
-                                 HandleScript script, Handle<LazyScript*> lazyScript,
+                                 SharedContext* sc, HandleScript script,
+                                 Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
     lazyScript(cx, lazyScript),
     prologue(cx, lineNum),
     main(cx, lineNum),
     current(&main),
-    parser(parser),
     atomIndices(cx->frontendCollectionPool()),
     firstLine(lineNum),
     maxFixedSlots(0),
     maxStackDepth(0),
     stackDepth(0),
     emitLevel(0),
     bodyScopeIndex(UINT32_MAX),
     varEmitterScope(nullptr),
@@ -2170,29 +2169,45 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     tryNoteList(cx),
     scopeNoteList(cx),
     yieldAndAwaitOffsetList(cx),
     typesetCount(0),
     hasSingletons(false),
     hasTryFinally(false),
     emittingRunOnceLambda(false),
     emitterMode(emitterMode),
+    scriptStartOffsetSet(false),
     functionBodyEndPosSet(false)
 {
     MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
+                                 BCEParserHandle* handle, SharedContext* sc,
+                                 HandleScript script, Handle<LazyScript*> lazyScript,
+                                 uint32_t lineNum, EmitterMode emitterMode)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode)
+{
+    parser = handle;
+}
+
+BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
+                                 const EitherParser& parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
-                                 TokenPos bodyPosition, EmitterMode emitterMode)
-    : BytecodeEmitter(parent, parser, sc, script, lazyScript,
-                      parser.tokenStream().srcCoords.lineNum(bodyPosition.begin),
-                      emitterMode)
-{
+                                 uint32_t lineNum, EmitterMode emitterMode)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode)
+{
+    ep_.emplace(parser);
+    this->parser = ep_.ptr();
+}
+
+void
+BytecodeEmitter::initFromBodyPosition(TokenPos bodyPosition)
+{
+    setScriptStartOffsetIfUnset(bodyPosition);
     setFunctionBodyEndPos(bodyPosition);
 }
 
 bool
 BytecodeEmitter::init()
 {
     return atomIndices.acquire(cx);
 }
@@ -2522,25 +2537,25 @@ LengthOfSetLine(unsigned line)
 {
     return 1 /* SRC_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
 }
 
 /* Updates line number notes, not column notes. */
 bool
 BytecodeEmitter::updateLineNumberNotes(uint32_t offset)
 {
-    TokenStreamAnyChars* ts = &parser.tokenStream();
+    ErrorReporter* er = &parser->errorReporter();
     bool onThisLine;
-    if (!ts->srcCoords.isOnThisLine(offset, currentLine(), &onThisLine)) {
-        ts->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
+    if (!er->isOnThisLine(offset, currentLine(), &onThisLine)) {
+        er->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
         return false;
     }
 
     if (!onThisLine) {
-        unsigned line = ts->srcCoords.lineNum(offset);
+        unsigned line = er->lineAt(offset);
         unsigned delta = line - currentLine();
 
         /*
          * Encode any change in the current source line number by using
          * either several SRC_NEWLINE notes or just one SRC_SETLINE note,
          * whichever consumes less space.
          *
          * NB: We handle backward line number deltas (possible with for
@@ -2566,17 +2581,17 @@ BytecodeEmitter::updateLineNumberNotes(u
 
 /* Updates the line number and column number information in the source notes. */
 bool
 BytecodeEmitter::updateSourceCoordNotes(uint32_t offset)
 {
     if (!updateLineNumberNotes(offset))
         return false;
 
-    uint32_t columnIndex = parser.tokenStream().srcCoords.columnIndex(offset);
+    uint32_t columnIndex = parser->errorReporter().columnAt(offset);
     ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(current->lastColumn);
     if (colspan != 0) {
         // If the column span is so large that we can't store it, then just
         // discard this information. This can happen with minimized or otherwise
         // machine-generated code. Even gigantic column numbers are still
         // valuable if you have a source map to relate them to something real;
         // but it's better to fail soft here.
         if (!SN_REPRESENTABLE_COLSPAN(colspan))
@@ -3519,100 +3534,54 @@ BytecodeEmitter::needsImplicitThis()
     for (EmitterScope* es = innermostEmitterScope; es; es = es->enclosingInFrame()) {
         if (es->scope(this)->kind() == ScopeKind::With)
             return true;
     }
 
     return false;
 }
 
-bool
-BytecodeEmitter::maybeSetDisplayURL()
-{
-    if (tokenStream().hasDisplayURL()) {
-        if (!parser.ss()->setDisplayURL(cx, tokenStream().displayURL()))
-            return false;
-    }
-    return true;
-}
-
-bool
-BytecodeEmitter::maybeSetSourceMap()
-{
-    if (tokenStream().hasSourceMapURL()) {
-        MOZ_ASSERT(!parser.ss()->hasSourceMapURL());
-        if (!parser.ss()->setSourceMapURL(cx, tokenStream().sourceMapURL()))
-            return false;
-    }
-
-    /*
-     * Source map URLs passed as a compile option (usually via a HTTP source map
-     * header) override any source map urls passed as comment pragmas.
-     */
-    if (parser.options().sourceMapURL()) {
-        // Warn about the replacement, but use the new one.
-        if (parser.ss()->hasSourceMapURL()) {
-            if (!parser.warningNoOffset(JSMSG_ALREADY_HAS_PRAGMA,
-                                        parser.ss()->filename(), "//# sourceMappingURL"))
-            {
-                return false;
-            }
-        }
-
-        if (!parser.ss()->setSourceMapURL(cx, parser.options().sourceMapURL()))
-            return false;
-    }
-
-    return true;
-}
-
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext* cx)
 {
     // Note: when parsing off thread the resulting scripts need to be handed to
     // the debugger after rejoining to the main thread.
     if (cx->helperThread())
         return;
 
     // Lazy scripts are never top level (despite always being invoked with a
     // nullptr parent), and so the hook should never be fired.
     if (emitterMode != LazyFunction && !parent)
         Debugger::onNewScript(cx, script);
 }
 
-inline TokenStreamAnyChars&
-BytecodeEmitter::tokenStream()
-{
-    return parser.tokenStream();
-}
-
 void
 BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
 {
-    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
+    MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
+    uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
     va_list args;
     va_start(args, errorNumber);
 
-    ErrorMetadata metadata;
-    if (parser.computeErrorMetadata(&metadata, pos.begin))
-        ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
+    parser->errorReporter().errorAtVA(offset, errorNumber, &args);
 
     va_end(args);
 }
 
 bool
 BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
 {
-    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
+    MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
+    uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
     va_list args;
     va_start(args, errorNumber);
 
-    bool result = parser.reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, &args);
+    bool result = parser->errorReporter().reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::emitNewInit(JSProtoKey key)
 {
@@ -3644,17 +3613,17 @@ BytecodeEmitter::iteratorResultShape(uns
 
     Rooted<jsid> value_id(cx, NameToId(cx->names().value));
     Rooted<jsid> done_id(cx, NameToId(cx->names().done));
     if (!NativeDefineDataProperty(cx, obj, value_id, UndefinedHandleValue, JSPROP_ENUMERATE))
         return false;
     if (!NativeDefineDataProperty(cx, obj, done_id, UndefinedHandleValue, JSPROP_ENUMERATE))
         return false;
 
-    ObjectBox* objbox = parser.newObjectBox(obj);
+    ObjectBox* objbox = parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
     *shape = objectList.add(objbox);
 
     return true;
 }
 
@@ -4465,17 +4434,17 @@ BytecodeEmitter::emitSwitch(ParseNode* p
     // After entering the scope, push the switch control.
     BreakableControl controlInfo(this, StatementKind::Switch);
 
     ptrdiff_t top = offset();
 
     // Switch bytecodes run from here till end of final case.
     uint32_t caseCount = cases->pn_count;
     if (caseCount > JS_BIT(16)) {
-        parser.reportError(JSMSG_TOO_MANY_CASES);
+        reportError(pn, JSMSG_TOO_MANY_CASES);
         return false;
     }
 
     // Try for most optimal, fall back if not dense ints.
     JSOp switchOp = JSOP_TABLESWITCH;
     uint32_t tableLength = 0;
     int32_t low, high;
     bool hasDefault = false;
@@ -4853,17 +4822,19 @@ BytecodeEmitter::emitSetThis(ParseNode* 
     }
 
     return emitSetOrInitializeNameAtLocation(name, lexicalLoc, emitRhs, true);
 }
 
 bool
 BytecodeEmitter::emitScript(ParseNode* body)
 {
-    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, tokenStream(), body);
+    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), body);
+
+    setScriptStartOffsetIfUnset(body->pn_pos);
 
     TDZCheckCache tdzCache(this);
     EmitterScope emitterScope(this);
     if (sc->isGlobalContext()) {
         switchToPrologue();
         if (!emitterScope.enterGlobal(this, sc->asGlobalContext()))
             return false;
         switchToMain();
@@ -4911,31 +4882,28 @@ BytecodeEmitter::emitScript(ParseNode* b
         return false;
 
     if (!emitterScope.leave(this))
         return false;
 
     if (!JSScript::fullyInitFromEmitter(cx, script, this))
         return false;
 
-    // URL and source map information must be set before firing
-    // Debugger::onNewScript.
-    if (!maybeSetDisplayURL() || !maybeSetSourceMap())
-        return false;
-
     tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitFunctionScript(ParseNode* body)
 {
     FunctionBox* funbox = sc->asFunctionBox();
-    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, tokenStream(), funbox);
+    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), funbox);
+
+    setScriptStartOffsetIfUnset(body->pn_pos);
 
     // The ordering of these EmitterScopes is important. The named lambda
     // scope needs to enclose the function scope needs to enclose the extra
     // var scope.
 
     Maybe<EmitterScope> namedLambdaEmitterScope;
     if (funbox->namedLambdaBindings()) {
         namedLambdaEmitterScope.emplace(this);
@@ -4978,25 +4946,17 @@ BytecodeEmitter::emitFunctionScript(Pars
         if (!namedLambdaEmitterScope->leave(this))
             return false;
         namedLambdaEmitterScope.reset();
     }
 
     if (!JSScript::fullyInitFromEmitter(cx, script, this))
         return false;
 
-    // URL and source map information must be set before firing
-    // Debugger::onNewScript. Only top-level functions need this, as compiling
-    // the outer scripts of nested functions already processed the source.
-    if (emitterMode != LazyFunction && !parent) {
-        if (!maybeSetDisplayURL() || !maybeSetSourceMap())
-            return false;
-
-        tellDebuggerAboutCompiledScript(cx);
-    }
+    tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 template <typename NameEmitter>
 bool
 BytecodeEmitter::emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitter emitName)
 {
@@ -6578,42 +6538,42 @@ BytecodeEmitter::emitSingletonInitialise
         (pn->getKind() == ParseNodeKind::Object) ? SingletonObject : TenuredObject;
 
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind))
         return false;
 
     MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton());
 
-    ObjectBox* objbox = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox = parser->newObjectBox(&value.toObject());
     if (!objbox)
         return false;
 
     return emitObjectOp(objbox, JSOP_OBJECT);
 }
 
 bool
 BytecodeEmitter::emitCallSiteObject(ParseNode* pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox1 = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox1 = parser->newObjectBox(&value.toObject());
     if (!objbox1)
         return false;
 
     if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox2 = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox2 = parser->newObjectBox(&value.toObject());
     if (!objbox2)
         return false;
 
     return emitObjectPairOp(objbox1, objbox2, JSOP_CALLSITEOBJ);
 }
 
 /* See the SRC_FOR source note offsetBias comments later in this file. */
 JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
@@ -7118,30 +7078,30 @@ BytecodeEmitter::emitInitializeForInOrOf
                "must have a per-iteration value for initializing");
 
     ParseNode* target = forHead->pn_kid1;
     MOZ_ASSERT(!forHead->pn_kid2);
 
     // If the for-in/of loop didn't have a variable declaration, per-loop
     // initialization is just assigning the iteration value to a target
     // expression.
-    if (!parser.isDeclarationList(target))
+    if (!parser->astGenerator().isDeclarationList(target))
         return emitAssignment(target, ParseNodeKind::Assign, nullptr); // ... ITERVAL
 
     // Otherwise, per-loop initialization is (possibly) declaration
     // initialization.  If the declaration is a lexical declaration, it must be
     // initialized.  If the declaration is a variable declaration, an
     // assignment to that name (which does *not* necessarily assign to the
     // variable!) must be generated.
 
     if (!updateSourceCoordNotes(target->pn_pos.begin))
         return false;
 
     MOZ_ASSERT(target->isForLoopDeclaration());
-    target = parser.singleBindingFromDeclaration(target);
+    target = parser->astGenerator().singleBindingFromDeclaration(target);
 
     if (target->isKind(ParseNodeKind::Name)) {
         auto emitSwapScopeAndRhs = [](BytecodeEmitter* bce, const NameLocation&,
                                       bool emittedBindOp)
         {
             if (emittedBindOp) {
                 // Per-iteration initialization in for-in/of loops computes the
                 // iteration value *before* initializing.  Thus the
@@ -7371,18 +7331,18 @@ BytecodeEmitter::emitForIn(ParseNode* fo
 
     ParseNode* forInHead = forInLoop->pn_left;
     MOZ_ASSERT(forInHead->isKind(ParseNodeKind::ForIn));
     MOZ_ASSERT(forInHead->isArity(PN_TERNARY));
 
     // Annex B: Evaluate the var-initializer expression if present.
     // |for (var i = initializer in expr) { ... }|
     ParseNode* forInTarget = forInHead->pn_kid1;
-    if (parser.isDeclarationList(forInTarget)) {
-        ParseNode* decl = parser.singleBindingFromDeclaration(forInTarget);
+    if (parser->astGenerator().isDeclarationList(forInTarget)) {
+        ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(forInTarget);
         if (decl->isKind(ParseNodeKind::Name)) {
             if (ParseNode* initializer = decl->expr()) {
                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
                            "for-in initializers are only permitted for |var| declarations");
 
                 if (!updateSourceCoordNotes(decl->pn_pos.begin))
                     return false;
 
@@ -7643,17 +7603,17 @@ BytecodeEmitter::emitCStyleFor(ParseNode
         if (!updateSourceCoordNotes(update->pn_pos.begin))
             return false;
         if (!emitTree(update, ValueUsage::IgnoreValue))
             return false;
         if (!emit1(JSOP_POP))
             return false;
 
         /* Restore the absolute line number for source note readers. */
-        uint32_t lineNum = parser.tokenStream().srcCoords.lineNum(pn->pn_pos.end);
+        uint32_t lineNum = parser->errorReporter().lineAt(pn->pn_pos.end);
         if (currentLine() != lineNum) {
             if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum)))
                 return false;
             current->currentLine = lineNum;
             current->lastColumn = 0;
         }
     }
 
@@ -7799,18 +7759,18 @@ BytecodeEmitter::emitFunction(ParseNode*
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
-            MOZ_ASSERT(parent->mutedErrors() == parser.options().mutedErrors());
-            const TransitiveCompileOptions& transitiveOptions = parser.options();
+            MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
+            const TransitiveCompileOptions& transitiveOptions = parser->options();
             CompileOptions options(cx, transitiveOptions);
 
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
                                                           funbox->bufStart, funbox->bufEnd,
                                                           funbox->toStringStart,
                                                           funbox->toStringEnd));
             if (!script)
@@ -8085,18 +8045,18 @@ BytecodeEmitter::emitWhile(ParseNode* pn
     // If we have a single-line while, like "while (x) ;", we want to
     // emit the line note before the initial goto, so that the
     // debugger sees a single entry point.  This way, if there is a
     // breakpoint on the line, it will only fire once; and "next"ing
     // will skip the whole loop.  However, for the multi-line case we
     // want to emit the line note after the initial goto, so that
     // "cont" stops on each iteration -- but without a stop before the
     // first iteration.
-    if (parser.tokenStream().srcCoords.lineNum(pn->pn_pos.begin) ==
-        parser.tokenStream().srcCoords.lineNum(pn->pn_pos.end))
+    if (parser->errorReporter().lineAt(pn->pn_pos.begin) ==
+        parser->errorReporter().lineAt(pn->pn_pos.end))
     {
         if (!updateSourceCoordNotes(pn->pn_pos.begin))
             return false;
     }
 
     JumpTarget top{ -1 };
     if (!emitJumpTarget(&top))
         return false;
@@ -9175,17 +9135,17 @@ BytecodeEmitter::emitCallee(ParseNode* c
         } else {
             if (!emitTree(callee))
                 return false;
         }
         *callop = false;
         break;
       case ParseNodeKind::SuperBase:
         MOZ_ASSERT(call->isKind(ParseNodeKind::SuperCall));
-        MOZ_ASSERT(parser.isSuperBase(callee));
+        MOZ_ASSERT(parser->astGenerator().isSuperBase(callee));
         if (!emit1(JSOP_SUPERFUN))
             return false;
         break;
       default:
         if (!emitTree(callee))
             return false;
         *callop = false;             /* trigger JSOP_UNDEFINED after */
         break;
@@ -9246,17 +9206,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
      * Then (or in a call case that has no explicit reference-base
      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
      * value required for calls (which non-strict mode functions
      * will box into the global object).
      */
     uint32_t argc = pn->pn_count - 1;
 
     if (argc >= ARGC_LIMIT) {
-        parser.reportError(callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
+        reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
     ParseNode* pn2 = pn->pn_head;
     bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
 
     if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
         // Calls to "forceInterpreter", "callFunction",
@@ -9390,17 +9350,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
             return false;
         checkTypeSet(pn->getOp());
     }
     if (pn->isOp(JSOP_EVAL) ||
         pn->isOp(JSOP_STRICTEVAL) ||
         pn->isOp(JSOP_SPREADEVAL) ||
         pn->isOp(JSOP_STRICTSPREADEVAL))
     {
-        uint32_t lineNum = parser.tokenStream().srcCoords.lineNum(pn->pn_pos.begin);
+        uint32_t lineNum = parser->errorReporter().lineAt(pn->pn_pos.begin);
         if (!emitUint32Operand(JSOP_LINENO, lineNum))
             return false;
     }
 
     return true;
 }
 
 static const JSOp ParseNodeKindToJSOp[] = {
@@ -9830,17 +9790,17 @@ BytecodeEmitter::emitObject(ParseNode* p
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
 {
-    ObjectBox* objbox = parser.newObjectBox(obj);
+    ObjectBox* objbox = parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
     static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
                   "newinit and newobject must have equal length to edit in-place");
 
     uint32_t index = objectList.add(objbox);
     jsbytecode* code = this->code(offset);
@@ -9879,17 +9839,17 @@ BytecodeEmitter::emitArrayLiteral(ParseN
                 // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the
                 // group for the template is accurate. We don't do this here as we
                 // want to use ObjectGroup::allocationSiteGroup, which requires a
                 // finished script.
                 JSObject* obj = &value.toObject();
                 MOZ_ASSERT(obj->is<ArrayObject>() &&
                            obj->as<ArrayObject>().denseElementsAreCopyOnWrite());
 
-                ObjectBox* objbox = parser.newObjectBox(obj);
+                ObjectBox* objbox = parser->newObjectBox(obj);
                 if (!objbox)
                     return false;
 
                 return emitObjectOp(objbox, JSOP_NEWARRAY_COPYONWRITE);
             }
         }
     }
 
@@ -11119,17 +11079,17 @@ BytecodeEmitter::addToSrcNoteDelta(jssrc
     }
     return true;
 }
 
 bool
 BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset)
 {
     if (!SN_REPRESENTABLE_OFFSET(offset)) {
-        parser.reportError(JSMSG_NEED_DIET, js_script_str);
+        reportError(nullptr, JSMSG_NEED_DIET, js_script_str);
         return false;
     }
 
     SrcNotesVector& notes = this->notes();
 
     /* Find the offset numbered which (i.e., skip exactly which offsets). */
     jssrcnote* sn = &notes[index];
     MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -7,16 +7,17 @@
 /* JS bytecode generation. */
 
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
 #include "mozilla/Attributes.h"
 
 #include "ds/InlineTable.h"
+#include "frontend/BCEParserHandle.h"
 #include "frontend/EitherParser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
@@ -215,17 +216,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
         EmitSection(JSContext* cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
             lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
         {}
     };
     EmitSection prologue, main, *current;
 
-    EitherParser<FullParseHandler> parser;
+    // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be initialized.
+    // Use |parser| instead.
+    mozilla::Maybe<EitherParser> ep_;
+    BCEParserHandle *parser;
 
     PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
 
     uint32_t        maxFixedSlots;  /* maximum number of fixed frame slots so far */
     uint32_t        maxStackDepth;  /* maximum number of expression stack slots so far */
 
     int32_t         stackDepth;     /* current stack depth in script frame */
@@ -276,50 +280,82 @@ struct MOZ_STACK_CLASS BytecodeEmitter
          * Check the static scope chain of the root function for resolving free
          * variable accesses in the script.
          */
         LazyFunction
     };
 
     const EmitterMode emitterMode;
 
+    MOZ_INIT_OUTSIDE_CTOR uint32_t scriptStartOffset;
+    bool scriptStartOffsetSet;
+
     // The end location of a function body that is being emitted.
     MOZ_INIT_OUTSIDE_CTOR uint32_t functionBodyEndPos;
     // Whether functionBodyEndPos was set.
     bool functionBodyEndPosSet;
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
-    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser<FullParseHandler>& parser,
-                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
-                    uint32_t lineNum, EmitterMode emitterMode = Normal);
+  private:
+    // Internal constructor, for delegation use only.
+    BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
+                    Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode);
+
+    void initFromBodyPosition(TokenPos bodyPosition);
+
+  public:
+
+    BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+                    EmitterMode emitterMode = Normal);
+
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+                    EmitterMode emitterMode = Normal);
 
     template<typename CharT>
     BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     uint32_t lineNum, EmitterMode emitterMode = Normal)
-      : BytecodeEmitter(parent, EitherParser<FullParseHandler>(parser), sc, script, lazyScript,
+      : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
                         lineNum, emitterMode)
     {}
 
     // An alternate constructor that uses a TokenPos for the starting
     // line and that sets functionBodyEndPos as well.
-    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser<FullParseHandler>& parser,
-                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
-                    TokenPos bodyPosition, EmitterMode emitterMode = Normal);
+    BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
+                    EmitterMode emitterMode = Normal)
+        : BytecodeEmitter(parent, parser, sc, script, lazyScript,
+                          parser->errorReporter().lineAt(bodyPosition.begin),
+                          emitterMode)
+    {
+        initFromBodyPosition(bodyPosition);
+    }
+
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
+                    EmitterMode emitterMode = Normal)
+        : BytecodeEmitter(parent, parser, sc, script, lazyScript,
+                          parser.errorReporter().lineAt(bodyPosition.begin),
+                          emitterMode)
+    {
+        initFromBodyPosition(bodyPosition);
+    }
 
     template<typename CharT>
     BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     TokenPos bodyPosition, EmitterMode emitterMode = Normal)
-      : BytecodeEmitter(parent, EitherParser<FullParseHandler>(parser), sc, script, lazyScript,
+      : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
                         bodyPosition, emitterMode)
     {}
 
     MOZ_MUST_USE bool init();
 
     template <typename Predicate /* (NestableControl*) -> bool */>
     NestableControl* findInnermostNestableControl(Predicate predicate) const;
 
@@ -376,22 +412,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool checkSingletonContext();
 
     // Check whether our function is in a run-once context (a toplevel
     // run-one script or a run-once lambda).
     MOZ_MUST_USE bool checkRunOnceContext();
 
     bool needsImplicitThis();
 
-    MOZ_MUST_USE bool maybeSetDisplayURL();
-    MOZ_MUST_USE bool maybeSetSourceMap();
     void tellDebuggerAboutCompiledScript(JSContext* cx);
 
-    inline TokenStreamAnyChars& tokenStream();
-
     BytecodeVector& code() const { return current->code; }
     jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; }
     ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
     ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); }
     void switchToMain() { current = &main; }
     void switchToPrologue() { current = &prologue; }
     bool inPrologue() const { return current == &prologue; }
 
@@ -412,16 +444,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter
         return lastOpcodeIsJumpTarget() ? current->lastTarget.offset : offset();
     }
 
     void setFunctionBodyEndPos(TokenPos pos) {
         functionBodyEndPos = pos.end;
         functionBodyEndPosSet = true;
     }
 
+    void setScriptStartOffsetIfUnset(TokenPos pos) {
+        if (!scriptStartOffsetSet) {
+            scriptStartOffset = pos.begin;
+            scriptStartOffsetSet = true;
+        }
+    }
+
     void reportError(ParseNode* pn, unsigned errorNumber, ...);
     bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...);
 
     // If pn contains a useful expression, return true with *answer set to true.
     // If pn contains a useless expression, return true with *answer set to
     // false. Return false on error.
     //
     // The caller should initialize *answer to false and invoke this function on
--- a/js/src/frontend/EitherParser.h
+++ b/js/src/frontend/EitherParser.h
@@ -15,16 +15,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Variant.h"
 
 #include <utility>
 
+#include "frontend/BCEParserHandle.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 
 namespace detail