Bug 1392352 - Part 2.3 - To fold: delete the old XBL implementation of tabbrowser;r=dao draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Tue, 27 Feb 2018 09:19:09 -0800
changeset 760462 24b6b40786e286e6b397dc83bf8f432a16cc708f
parent 760461 bfe1433b7ccb93db1fda0e59ae23bc41aeecfdaf
push id100651
push userbgrinstead@mozilla.com
push dateTue, 27 Feb 2018 17:19:18 +0000
reviewersdao
bugs1392352
milestone60.0a1
Bug 1392352 - Part 2.3 - To fold: delete the old XBL implementation of tabbrowser;r=dao MozReview-Commit-ID: DlT3pLuTUUC
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -46,6335 +46,16 @@
         gBrowser = new TabBrowser(this);
       </constructor>
       <destructor>
         gBrowser.disconnectedCallback();
       </destructor>
     </implementation>
   </binding>
 
-  <binding id="tabbrowser-TO-DELETE">
-    <resources>
-      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
-    </resources>
-
-    <content>
-      <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
-      <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
-                  flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown,tabcontainer"
-                  onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
-        <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
-          <xul:notificationbox flex="1" notificationside="top">
-            <xul:hbox flex="1" class="browserSidebarContainer">
-              <xul:vbox flex="1" class="browserContainer">
-                <xul:stack flex="1" class="browserStack" anonid="browserStack">
-                  <xul:browser anonid="initialBrowser" type="content" message="true" messagemanagergroup="browsers"
-                               primary="true" blank="true"
-                               xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker"/>
-                </xul:stack>
-              </xul:vbox>
-            </xul:hbox>
-          </xul:notificationbox>
-        </xul:tabpanels>
-      </xul:tabbox>
-      <children/>
-    </content>
-    <implementation implements="nsIDOMEventListener, nsIMessageListener, nsIObserver">
-
-      <property name="tabContextMenu" readonly="true"
-                onget="return this.tabContainer.contextMenu;"/>
-
-      <field name="tabContainer" readonly="true">
-        document.getElementById(this.getAttribute("tabcontainer"));
-      </field>
-      <field name="tabs" readonly="true">
-        this.tabContainer.childNodes;
-      </field>
-
-      <property name="visibleTabs" readonly="true">
-        <getter><![CDATA[
-          if (!this._visibleTabs)
-            this._visibleTabs = Array.filter(this.tabs,
-                                             tab => !tab.hidden && !tab.closing);
-          return this._visibleTabs;
-        ]]></getter>
-      </property>
-
-      <field name="closingTabsEnum" readonly="true">({ ALL: 0, OTHER: 1, TO_END: 2 });</field>
-
-      <field name="_visibleTabs">null</field>
-
-      <field name="mURIFixup" readonly="true">
-        Components.classes["@mozilla.org/docshell/urifixup;1"]
-                  .getService(Components.interfaces.nsIURIFixup);
-      </field>
-      <field name="_unifiedComplete" readonly="true">
-         Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
-                   .getService(Components.interfaces.mozIPlacesAutoComplete);
-      </field>
-      <field name="tabbox" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
-      </field>
-      <field name="mPanelContainer" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
-      </field>
-      <field name="mCurrentTab">
-        null
-      </field>
-      <field name="_lastRelatedTabMap">
-        new WeakMap();
-      </field>
-      <field name="mCurrentBrowser">
-        null
-      </field>
-      <field name="mProgressListeners">
-        []
-      </field>
-      <field name="mTabsProgressListeners">
-        []
-      </field>
-      <field name="_tabListeners">
-        new Map()
-      </field>
-      <field name="_tabFilters">
-        new Map()
-      </field>
-      <field name="mIsBusy">
-        false
-      </field>
-      <field name="_outerWindowIDBrowserMap">
-        new Map();
-      </field>
-      <field name="arrowKeysShouldWrap" readonly="true">
-        AppConstants == "macosx";
-      </field>
-
-      <field name="_autoScrollPopup">
-        null
-      </field>
-
-      <field name="_previewMode">
-        false
-      </field>
-
-      <field name="_lastFindValue">
-        ""
-      </field>
-
-      <field name="_contentWaitingCount">
-        0
-      </field>
-
-      <field name="tabAnimationsInProgress">
-        0
-      </field>
-
-      <property name="_numPinnedTabs" readonly="true">
-        <getter><![CDATA[
-          for (var i = 0; i < this.tabs.length; i++) {
-            if (!this.tabs[i].pinned)
-              break;
-          }
-          return i;
-        ]]></getter>
-      </property>
-
-      <property name="popupAnchor" readonly="true">
-        <getter><![CDATA[
-        if (this.mCurrentTab._popupAnchor) {
-          return this.mCurrentTab._popupAnchor;
-        }
-        let stack = this.mCurrentBrowser.parentNode;
-        // Create an anchor for the popup
-        const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-        let popupAnchor = document.createElementNS(NS_XUL, "hbox");
-        popupAnchor.className = "popup-anchor";
-        popupAnchor.hidden = true;
-        stack.appendChild(popupAnchor);
-        return this.mCurrentTab._popupAnchor = popupAnchor;
-        ]]></getter>
-      </property>
-
-      <method name="isFindBarInitialized">
-        <parameter name="aTab"/>
-        <body><![CDATA[
-          return (aTab || this.selectedTab)._findBar != undefined;
-        ]]></body>
-      </method>
-
-      <method name="getFindBar">
-        <parameter name="aTab"/>
-        <body><![CDATA[
-          if (!aTab)
-            aTab = this.selectedTab;
-
-          if (aTab._findBar)
-            return aTab._findBar;
-
-          let findBar = document.createElementNS(this.namespaceURI, "findbar");
-          let browser = this.getBrowserForTab(aTab);
-          let browserContainer = this.getBrowserContainer(browser);
-          browserContainer.appendChild(findBar);
-
-          // Force a style flush to ensure that our binding is attached.
-          findBar.clientTop;
-
-          findBar.browser = browser;
-          findBar._findField.value = this._lastFindValue;
-
-          aTab._findBar = findBar;
-
-          let event = document.createEvent("Events");
-          event.initEvent("TabFindInitialized", true, false);
-          aTab.dispatchEvent(event);
-
-          return findBar;
-        ]]></body>
-      </method>
-
-      <method name="getStatusPanel">
-        <body><![CDATA[
-          if (!this._statusPanel) {
-            this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel");
-            this._statusPanel.setAttribute("inactive", "true");
-            this._statusPanel.setAttribute("layer", "true");
-            this._appendStatusPanel();
-          }
-          return this._statusPanel;
-        ]]></body>
-      </method>
-
-      <method name="_appendStatusPanel">
-        <body><![CDATA[
-          if (this._statusPanel) {
-            let browser = this.selectedBrowser;
-            let browserContainer = this.getBrowserContainer(browser);
-            browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
-          }
-        ]]></body>
-      </method>
-
-      <method name="pinTab">
-        <parameter name="aTab"/>
-        <body><![CDATA[
-          if (aTab.pinned)
-            return;
-
-          if (aTab.hidden)
-            this.showTab(aTab);
-
-          this.moveTabTo(aTab, this._numPinnedTabs);
-          aTab.setAttribute("pinned", "true");
-          this.tabContainer._unlockTabSizing();
-          this.tabContainer._positionPinnedTabs();
-          this.tabContainer._updateCloseButtons();
-
-          this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: true });
-
-          let event = document.createEvent("Events");
-          event.initEvent("TabPinned", true, false);
-          aTab.dispatchEvent(event);
-        ]]></body>
-      </method>
-
-      <method name="unpinTab">
-        <parameter name="aTab"/>
-        <body><![CDATA[
-          if (!aTab.pinned)
-            return;
-
-          this.moveTabTo(aTab, this._numPinnedTabs - 1);
-          aTab.removeAttribute("pinned");
-          aTab.style.marginInlineStart = "";
-          this.tabContainer._unlockTabSizing();
-          this.tabContainer._positionPinnedTabs();
-          this.tabContainer._updateCloseButtons();
-
-          this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: false });
-
-          let event = document.createEvent("Events");
-          event.initEvent("TabUnpinned", true, false);
-          aTab.dispatchEvent(event);
-        ]]></body>
-      </method>
-
-      <method name="previewTab">
-        <parameter name="aTab"/>
-        <parameter name="aCallback"/>
-        <body>
-          <![CDATA[
-            let currentTab = this.selectedTab;
-            try {
-              // Suppress focus, ownership and selected tab changes
-              this._previewMode = true;
-              this.selectedTab = aTab;
-              aCallback();
-            } finally {
-              this.selectedTab = currentTab;
-              this._previewMode = false;
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="syncThrobberAnimations">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            aTab.ownerGlobal.promiseDocumentFlushed(() => {
-              if (!aTab.parentNode) {
-                return;
-              }
-
-              const animations =
-                Array.from(aTab.parentNode.getElementsByTagName("tab"))
-                .map(tab => {
-                  const throbber =
-                    document.getAnonymousElementByAttribute(tab, "anonid", "tab-throbber");
-                  return throbber ? throbber.getAnimations({ subtree: true }) : [];
-                })
-                .reduce((a, b) => a.concat(b))
-                .filter(anim =>
-                  anim instanceof CSSAnimation &&
-                  (anim.animationName === "tab-throbber-animation" ||
-                   anim.animationName === "tab-throbber-animation-rtl") &&
-                  (anim.playState === "running" || anim.playState === "pending"));
-
-              // Synchronize with the oldest running animation, if any.
-              const firstStartTime = Math.min(
-                ...animations.map(anim => anim.startTime === null ? Infinity : anim.startTime)
-              );
-              if (firstStartTime === Infinity) {
-                return;
-              }
-              requestAnimationFrame(() => {
-                for (let animation of animations) {
-                  // If |animation| has been cancelled since this rAF callback
-                  // was scheduled we don't want to set its startTime since
-                  // that would restart it. We check for a cancelled animation
-                  // by looking for a null currentTime rather than checking
-                  // the playState, since reading the playState of
-                  // a CSSAnimation object will flush style.
-                  if (animation.currentTime !== null) {
-                    animation.startTime = firstStartTime;
-                  }
-                }
-              });
-            });
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserAtIndex">
-        <parameter name="aIndex"/>
-        <body>
-          <![CDATA[
-            return this.browsers[aIndex];
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserIndexForDocument">
-        <parameter name="aDocument"/>
-        <body>
-          <![CDATA[
-            var tab = this._getTabForContentWindow(aDocument.defaultView);
-            return tab ? tab._tPos : -1;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserForDocument">
-        <parameter name="aDocument"/>
-        <body>
-          <![CDATA[
-            var tab = this._getTabForContentWindow(aDocument.defaultView);
-            return tab ? tab.linkedBrowser : null;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserForContentWindow">
-        <parameter name="aWindow"/>
-        <body>
-          <![CDATA[
-            var tab = this._getTabForContentWindow(aWindow);
-            return tab ? tab.linkedBrowser : null;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserForOuterWindowID">
-        <parameter name="aID"/>
-        <body>
-          <![CDATA[
-            return this._outerWindowIDBrowserMap.get(aID);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_getTabForContentWindow">
-        <parameter name="aWindow"/>
-        <body>
-        <![CDATA[
-          // When not using remote browsers, we can take a fast path by getting
-          // directly from the content window to the browser without looping
-          // over all browsers.
-          if (!gMultiProcessBrowser) {
-            let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell)
-                                 .chromeEventHandler;
-            return this.getTabForBrowser(browser);
-          }
-
-          for (let i = 0; i < this.browsers.length; i++) {
-            // NB: We use contentWindowAsCPOW so that this code works both
-            // for remote browsers as well. aWindow may be a CPOW.
-            if (this.browsers[i].contentWindowAsCPOW == aWindow)
-              return this.tabs[i];
-          }
-          return null;
-        ]]>
-        </body>
-      </method>
-
-      <!-- Binding from browser to tab -->
-      <field name="_tabForBrowser" readonly="true">
-      <![CDATA[
-        new WeakMap();
-      ]]>
-      </field>
-
-      <method name="getTabForBrowser">
-        <parameter name="aBrowser"/>
-        <body>
-        <![CDATA[
-          return this._tabForBrowser.get(aBrowser);
-        ]]>
-        </body>
-      </method>
-
-      <method name="getNotificationBox">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            return this.getSidebarContainer(aBrowser).parentNode;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getSidebarContainer">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            return this.getBrowserContainer(aBrowser).parentNode;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserContainer">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getTabModalPromptBox">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            let browser = (aBrowser || this.mCurrentBrowser);
-            if (!browser.tabModalPromptBox) {
-              browser.tabModalPromptBox = new TabModalPromptBox(browser);
-            }
-            return browser.tabModalPromptBox;
-          ]]>
-        </body>
-      </method>
-
-      <method name="getTabFromAudioEvent">
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
-              !aEvent.isTrusted) {
-            return null;
-          }
-
-          var browser = aEvent.originalTarget;
-          var tab = this.getTabForBrowser(browser);
-          return tab;
-        ]]>
-        </body>
-      </method>
-
-      <method name="_callProgressListeners">
-        <parameter name="aBrowser"/>
-        <parameter name="aMethod"/>
-        <parameter name="aArguments"/>
-        <parameter name="aCallGlobalListeners"/>
-        <parameter name="aCallTabsListeners"/>
-        <body><![CDATA[
-          var rv = true;
-
-          function callListeners(listeners, args) {
-            for (let p of listeners) {
-              if (aMethod in p) {
-                try {
-                  if (!p[aMethod].apply(p, args))
-                    rv = false;
-                } catch (e) {
-                  // don't inhibit other listeners
-                  Components.utils.reportError(e);
-                }
-              }
-            }
-          }
-
-          if (!aBrowser)
-            aBrowser = this.mCurrentBrowser;
-
-          // eslint-disable-next-line mozilla/no-compare-against-boolean-literals
-          if (aCallGlobalListeners != false &&
-              aBrowser == this.mCurrentBrowser) {
-            callListeners(this.mProgressListeners, aArguments);
-          }
-
-          // eslint-disable-next-line mozilla/no-compare-against-boolean-literals
-          if (aCallTabsListeners != false) {
-            aArguments.unshift(aBrowser);
-
-            callListeners(this.mTabsProgressListeners, aArguments);
-          }
-
-          return rv;
-        ]]></body>
-      </method>
-
-      <!-- Determine if a URI is an about: page pointing to a local resource. -->
-      <method name="_isLocalAboutURI">
-        <parameter name="aURI"/>
-        <parameter name="aResolvedURI"/>
-        <body><![CDATA[
-          if (!aURI.schemeIs("about")) {
-            return false;
-          }
-
-          // Specially handle about:blank as local
-          if (aURI.pathQueryRef === "blank") {
-            return true;
-          }
-
-          try {
-            // Use the passed in resolvedURI if we have one
-            const resolvedURI = aResolvedURI || Services.io.newChannelFromURI2(
-              aURI,
-              null, // loadingNode
-              Services.scriptSecurityManager.getSystemPrincipal(), // loadingPrincipal
-              null, // triggeringPrincipal
-              Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, // securityFlags
-              Ci.nsIContentPolicy.TYPE_OTHER // contentPolicyType
-            ).URI;
-            return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file");
-          } catch (ex) {
-            // aURI might be invalid.
-            return false;
-          }
-        ]]></body>
-      </method>
-
-      <!-- A web progress listener object definition for a given tab. -->
-      <method name="mTabProgressListener">
-        <parameter name="aTab"/>
-        <parameter name="aBrowser"/>
-        <parameter name="aStartsBlank"/>
-        <parameter name="aWasPreloadedBrowser"/>
-        <parameter name="aOrigStateFlags"/>
-        <body>
-        <![CDATA[
-          let stateFlags = aOrigStateFlags || 0;
-          // Initialize mStateFlags to non-zero e.g. when creating a progress
-          // listener for preloaded browsers as there was no progress listener
-          // around when the content started loading. If the content didn't
-          // quite finish loading yet, mStateFlags will very soon be overridden
-          // with the correct value and end up at STATE_STOP again.
-          if (aWasPreloadedBrowser) {
-            stateFlags = Ci.nsIWebProgressListener.STATE_STOP |
-                         Ci.nsIWebProgressListener.STATE_IS_REQUEST;
-          }
-
-          return ({
-            mTabBrowser: this,
-            mTab: aTab,
-            mBrowser: aBrowser,
-            mBlank: aStartsBlank,
-
-            // cache flags for correct status UI update after tab switching
-            mStateFlags: stateFlags,
-            mStatus: 0,
-            mMessage: "",
-            mTotalProgress: 0,
-
-            // count of open requests (should always be 0 or 1)
-            mRequestCount: 0,
-
-            destroy() {
-              delete this.mTab;
-              delete this.mBrowser;
-              delete this.mTabBrowser;
-            },
-
-            _callProgressListeners() {
-              Array.unshift(arguments, this.mBrowser);
-              return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
-            },
-
-            _shouldShowProgress(aRequest) {
-              if (this.mBlank)
-                return false;
-
-              // Don't show progress indicators in tabs for about: URIs
-              // pointing to local resources.
-              if ((aRequest instanceof Ci.nsIChannel) &&
-                  this.mTabBrowser._isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
-                return false;
-              }
-
-              return true;
-            },
-
-            _isForInitialAboutBlank(aWebProgress, aStateFlags, aLocation) {
-              if (!this.mBlank || !aWebProgress.isTopLevel) {
-                return false;
-              }
-
-              // If the state has STATE_STOP, and no requests were in flight, then this
-              // must be the initial "stop" for the initial about:blank document.
-              const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
-              if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
-                  this.mRequestCount == 0 &&
-                  !aLocation) {
-                return true;
-              }
-
-              let location = aLocation ? aLocation.spec : "";
-              return location == "about:blank";
-            },
-
-            onProgressChange(aWebProgress, aRequest,
-                             aCurSelfProgress, aMaxSelfProgress,
-                             aCurTotalProgress, aMaxTotalProgress) {
-              this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
-
-              if (!this._shouldShowProgress(aRequest))
-                return;
-
-              if (this.mTotalProgress && this.mTab.hasAttribute("busy"))
-                this.mTab.setAttribute("progress", "true");
-
-              this._callProgressListeners("onProgressChange",
-                                          [aWebProgress, aRequest,
-                                           aCurSelfProgress, aMaxSelfProgress,
-                                           aCurTotalProgress, aMaxTotalProgress]);
-            },
-
-            onProgressChange64(aWebProgress, aRequest,
-                               aCurSelfProgress, aMaxSelfProgress,
-                               aCurTotalProgress, aMaxTotalProgress) {
-              return this.onProgressChange(aWebProgress, aRequest,
-                aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
-                aMaxTotalProgress);
-            },
-
-            /* eslint-disable complexity */
-            onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-              if (!aRequest)
-                return;
-
-              const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
-              const nsIChannel = Components.interfaces.nsIChannel;
-              let location, originalLocation;
-              try {
-                aRequest.QueryInterface(nsIChannel);
-                location = aRequest.URI;
-                originalLocation = aRequest.originalURI;
-              } catch (ex) {}
-
-              let ignoreBlank = this._isForInitialAboutBlank(aWebProgress, aStateFlags,
-                                                             location);
-
-              // If we were ignoring some messages about the initial about:blank, and we
-              // got the STATE_STOP for it, we'll want to pay attention to those messages
-              // from here forward. Similarly, if we conclude that this state change
-              // is one that we shouldn't be ignoring, then stop ignoring.
-              if ((ignoreBlank &&
-                   aStateFlags & nsIWebProgressListener.STATE_STOP &&
-                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) ||
-                  !ignoreBlank && this.mBlank) {
-                this.mBlank = false;
-              }
-
-              if (aStateFlags & nsIWebProgressListener.STATE_START) {
-                this.mRequestCount++;
-              } else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
-                const NS_ERROR_UNKNOWN_HOST = 2152398878;
-                if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
-                  // to prevent bug 235825: wait for the request handled
-                  // by the automatic keyword resolver
-                  return;
-                }
-                // since we (try to) only handle STATE_STOP of the last request,
-                // the count of open requests should now be 0
-                this.mRequestCount = 0;
-              }
-
-              if (aStateFlags & nsIWebProgressListener.STATE_START &&
-                  aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
-                if (aWebProgress.isTopLevel) {
-                  // Need to use originalLocation rather than location because things
-                  // like about:home and about:privatebrowsing arrive with nsIRequest
-                  // pointing to their resolved jar: or file: URIs.
-                  if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
-                        originalLocation != "about:blank" &&
-                        this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec &&
-                        this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
-                    // Indicating that we started a load will allow the location
-                    // bar to be cleared when the load finishes.
-                    // In order to not overwrite user-typed content, we avoid it
-                    // (see if condition above) in a very specific case:
-                    // If the load is of an 'initial' page (e.g. about:privatebrowsing,
-                    // about:newtab, etc.), was not explicitly typed in the location
-                    // bar by the user, is not about:blank (because about:blank can be
-                    // loaded by websites under their principal), and the current
-                    // page in the browser is about:blank (indicating it is a newly
-                    // created or re-created browser, e.g. because it just switched
-                    // remoteness or is a new tab/window).
-                    this.mBrowser.urlbarChangeTracker.startedLoad();
-                  }
-                  delete this.mBrowser.initialPageLoadedFromURLBar;
-                  // If the browser is loading it must not be crashed anymore
-                  this.mTab.removeAttribute("crashed");
-                }
-
-                if (this._shouldShowProgress(aRequest)) {
-                  if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING) &&
-                      aWebProgress && aWebProgress.isTopLevel) {
-                    this.mTab.setAttribute("busy", "true");
-                    this.mTab._notselectedsinceload = !this.mTab.selected;
-                    SchedulePressure.startMonitoring(window, {
-                      highPressureFn() {
-                        // Only switch back to the SVG loading indicator after getting
-                        // three consecutive low pressure callbacks. Used to prevent
-                        // switching quickly between the SVG and APNG loading indicators.
-                        gBrowser.tabContainer._schedulePressureCount = gBrowser.schedulePressureDefaultCount;
-                        gBrowser.tabContainer.setAttribute("schedulepressure", "true");
-                      },
-                      lowPressureFn() {
-                        if (!gBrowser.tabContainer._schedulePressureCount ||
-                            --gBrowser.tabContainer._schedulePressureCount <= 0) {
-                          gBrowser.tabContainer.removeAttribute("schedulepressure");
-                        }
-
-                        // If tabs are closed while they are loading we need to
-                        // stop monitoring schedule pressure. We don't stop monitoring
-                        // during high pressure times because we want to eventually
-                        // return to the SVG tab loading animations.
-                        let continueMonitoring = true;
-                        if (!document.querySelector(".tabbrowser-tab[busy]")) {
-                          SchedulePressure.stopMonitoring(window);
-                          continueMonitoring = false;
-                        }
-                        return {continueMonitoring};
-                      },
-                    });
-                    this.mTabBrowser.syncThrobberAnimations(this.mTab);
-                  }
-
-                  if (this.mTab.selected) {
-                    this.mTabBrowser.mIsBusy = true;
-                  }
-                }
-              } else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
-                         aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
-
-                if (this.mTab.hasAttribute("busy")) {
-                  this.mTab.removeAttribute("busy");
-                  if (!document.querySelector(".tabbrowser-tab[busy]")) {
-                    SchedulePressure.stopMonitoring(window);
-                    this.mTabBrowser.tabContainer.removeAttribute("schedulepressure");
-                  }
-
-                  // Only animate the "burst" indicating the page has loaded if
-                  // the top-level page is the one that finished loading.
-                  if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
-                      Components.isSuccessCode(aStatus) &&
-                      !this.mTabBrowser.tabAnimationsInProgress &&
-                      Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
-                    if (this.mTab._notselectedsinceload) {
-                      this.mTab.setAttribute("notselectedsinceload", "true");
-                    } else {
-                      this.mTab.removeAttribute("notselectedsinceload");
-                    }
-
-                    this.mTab.setAttribute("bursting", "true");
-                  }
-
-                  this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
-                  if (!this.mTab.selected)
-                    this.mTab.setAttribute("unread", "true");
-                }
-                this.mTab.removeAttribute("progress");
-
-                if (aWebProgress.isTopLevel) {
-                  let isSuccessful = Components.isSuccessCode(aStatus);
-                  if (!isSuccessful && !isTabEmpty(this.mTab)) {
-                    // Restore the current document's location in case the
-                    // request was stopped (possibly from a content script)
-                    // before the location changed.
-
-                    this.mBrowser.userTypedValue = null;
-
-                    let inLoadURI = this.mBrowser.inLoadURI;
-                    if (this.mTab.selected && gURLBar && !inLoadURI) {
-                      URLBarSetURI();
-                    }
-                  } else if (isSuccessful) {
-                    this.mBrowser.urlbarChangeTracker.finishedLoad();
-                  }
-
-                  // Ignore initial about:blank to prevent flickering.
-                  if (!this.mBrowser.mIconURL && !ignoreBlank) {
-                    // Don't switch to the default icon on about:home or about:newtab,
-                    // since these pages get their favicon set in browser code to
-                    // improve perceived performance.
-                    let isNewTab = originalLocation &&
-                      (originalLocation.spec == "about:newtab" ||
-                       originalLocation.spec == "about:privatebrowsing" ||
-                       originalLocation.spec == "about:home");
-                    if (!isNewTab) {
-                      this.mTabBrowser.useDefaultIcon(this.mTab);
-                    }
-                  }
-                }
-
-                // For keyword URIs clear the user typed value since they will be changed into real URIs
-                if (location.scheme == "keyword")
-                  this.mBrowser.userTypedValue = null;
-
-                if (this.mTab.selected)
-                  this.mTabBrowser.mIsBusy = false;
-              }
-
-              if (ignoreBlank) {
-                this._callProgressListeners("onUpdateCurrentBrowser",
-                                            [aStateFlags, aStatus, "", 0],
-                                            true, false);
-              } else {
-                this._callProgressListeners("onStateChange",
-                                            [aWebProgress, aRequest, aStateFlags, aStatus],
-                                            true, false);
-              }
-
-              this._callProgressListeners("onStateChange",
-                                          [aWebProgress, aRequest, aStateFlags, aStatus],
-                                          false);
-
-              if (aStateFlags & (nsIWebProgressListener.STATE_START |
-                                 nsIWebProgressListener.STATE_STOP)) {
-                // reset cached temporary values at beginning and end
-                this.mMessage = "";
-                this.mTotalProgress = 0;
-              }
-              this.mStateFlags = aStateFlags;
-              this.mStatus = aStatus;
-            },
-            /* eslint-enable complexity */
-
-            onLocationChange(aWebProgress, aRequest, aLocation,
-                             aFlags) {
-              // OnLocationChange is called for both the top-level content
-              // and the subframes.
-              let topLevel = aWebProgress.isTopLevel;
-
-              if (topLevel) {
-                let isSameDocument =
-                  !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
-                // We need to clear the typed value
-                // if the document failed to load, to make sure the urlbar reflects the
-                // failed URI (particularly for SSL errors). However, don't clear the value
-                // if the error page's URI is about:blank, because that causes complete
-                // loss of urlbar contents for invalid URI errors (see bug 867957).
-                // Another reason to clear the userTypedValue is if this was an anchor
-                // navigation initiated by the user.
-                if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
-                    ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
-                     aLocation.spec != "about:blank") ||
-                    (isSameDocument && this.mBrowser.inLoadURI)) {
-                  this.mBrowser.userTypedValue = null;
-                }
-
-                // If the tab has been set to "busy" outside the stateChange
-                // handler below (e.g. by sessionStore.navigateAndRestore), and
-                // the load results in an error page, it's possible that there
-                // isn't any (STATE_IS_NETWORK & STATE_STOP) state to cause busy
-                // attribute being removed. In this case we should remove the
-                // attribute here.
-                if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
-                    this.mTab.hasAttribute("busy")) {
-                  this.mTab.removeAttribute("busy");
-                  this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
-                }
-
-                // If the browser was playing audio, we should remove the playing state.
-                if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
-                  clearTimeout(this.mTab._soundPlayingAttrRemovalTimer);
-                  this.mTab._soundPlayingAttrRemovalTimer = 0;
-                  this.mTab.removeAttribute("soundplaying");
-                  this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
-                }
-
-                // If the browser was previously muted, we should restore the muted state.
-                if (this.mTab.hasAttribute("muted")) {
-                  this.mTab.linkedBrowser.mute();
-                }
-
-                if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
-                  let findBar = this.mTabBrowser.getFindBar(this.mTab);
-
-                  // Close the Find toolbar if we're in old-style TAF mode
-                  if (findBar.findMode != findBar.FIND_NORMAL) {
-                    findBar.close();
-                  }
-                }
-
-                this.mTabBrowser.setTabTitle(this.mTab);
-
-                // Don't clear the favicon if this tab is in the pending
-                // state, as SessionStore will have set the icon for us even
-                // though we're pointed at an about:blank. Also don't clear it
-                // if onLocationChange was triggered by a pushState or a
-                // replaceState (bug 550565) or a hash change (bug 408415).
-                if (!this.mTab.hasAttribute("pending") &&
-                    aWebProgress.isLoadingDocument &&
-                    !isSameDocument) {
-                  this.mBrowser.mIconURL = null;
-                }
-
-                let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
-                if (this.mBrowser.registeredOpenURI) {
-                  this.mTabBrowser._unifiedComplete
-                                  .unregisterOpenPage(this.mBrowser.registeredOpenURI,
-                                                      userContextId);
-                  delete this.mBrowser.registeredOpenURI;
-                }
-                // Tabs in private windows aren't registered as "Open" so
-                // that they don't appear as switch-to-tab candidates.
-                if (!isBlankPageURL(aLocation.spec) &&
-                    (!PrivateBrowsingUtils.isWindowPrivate(window) ||
-                    PrivateBrowsingUtils.permanentPrivateBrowsing)) {
-                  this.mTabBrowser._unifiedComplete
-                                  .registerOpenPage(aLocation, userContextId);
-                  this.mBrowser.registeredOpenURI = aLocation;
-                }
-              }
-
-              if (!this.mBlank) {
-                this._callProgressListeners("onLocationChange",
-                                            [aWebProgress, aRequest, aLocation,
-                                             aFlags]);
-              }
-
-              if (topLevel) {
-                this.mBrowser.lastURI = aLocation;
-                this.mBrowser.lastLocationChange = Date.now();
-              }
-            },
-
-            onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-              if (this.mBlank)
-                return;
-
-              this._callProgressListeners("onStatusChange",
-                                          [aWebProgress, aRequest, aStatus, aMessage]);
-
-              this.mMessage = aMessage;
-            },
-
-            onSecurityChange(aWebProgress, aRequest, aState) {
-              this._callProgressListeners("onSecurityChange",
-                                          [aWebProgress, aRequest, aState]);
-            },
-
-            onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
-              return this._callProgressListeners("onRefreshAttempted",
-                                                 [aWebProgress, aURI, aDelay, aSameURI]);
-            },
-
-            QueryInterface(aIID) {
-              if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
-                  aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
-                  aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
-                  aIID.equals(Components.interfaces.nsISupports))
-                return this;
-              throw Components.results.NS_NOINTERFACE;
-            }
-          });
-        ]]>
-        </body>
-      </method>
-
-      <field name="serializationHelper">
-        Cc["@mozilla.org/network/serialization-helper;1"]
-          .getService(Ci.nsISerializationHelper);
-      </field>
-
-      <method name="storeIcon">
-        <parameter name="aBrowser"/>
-        <parameter name="aURI"/>
-        <parameter name="aLoadingPrincipal"/>
-        <parameter name="aRequestContextID"/>
-        <body>
-          <![CDATA[
-          try {
-            if (!(aURI instanceof Ci.nsIURI)) {
-              aURI = makeURI(aURI);
-            }
-            PlacesUIUtils.loadFavicon(aBrowser, aLoadingPrincipal, aURI, aRequestContextID);
-          } catch (ex) {
-            Components.utils.reportError(ex);
-          }
-        ]]>
-        </body>
-      </method>
-
-      <method name="setIcon">
-        <parameter name="aTab"/>
-        <parameter name="aURI"/>
-        <parameter name="aLoadingPrincipal"/>
-        <parameter name="aRequestContextID"/>
-        <body>
-          <![CDATA[
-            let browser = this.getBrowserForTab(aTab);
-            browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
-            let loadingPrincipal = aLoadingPrincipal ||
-                                   Services.scriptSecurityManager.getSystemPrincipal();
-            let requestContextID = aRequestContextID || 0;
-            let sizedIconUrl = browser.mIconURL || "";
-            if (sizedIconUrl != aTab.getAttribute("image")) {
-              if (sizedIconUrl) {
-                if (!browser.mIconLoadingPrincipal ||
-                    !browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
-                  aTab.setAttribute("iconloadingprincipal",
-                    this.serializationHelper.serializeToString(loadingPrincipal));
-                  aTab.setAttribute("requestcontextid", requestContextID);
-                  browser.mIconLoadingPrincipal = loadingPrincipal;
-                }
-                aTab.setAttribute("image", sizedIconUrl);
-              } else {
-                aTab.removeAttribute("iconloadingprincipal");
-                delete browser.mIconLoadingPrincipal;
-                aTab.removeAttribute("image");
-              }
-              this._tabAttrModified(aTab, ["image"]);
-            }
-
-            this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
-          ]]>
-        </body>
-      </method>
-
-      <method name="getIcon">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
-            return browser.mIconURL;
-          ]]>
-        </body>
-      </method>
-
-      <method name="setPageInfo">
-        <parameter name="aURL"/>
-        <parameter name="aDescription"/>
-        <parameter name="aPreviewImage"/>
-        <body>
-          <![CDATA[
-            if (aURL) {
-              let pageInfo = {url: aURL, description: aDescription, previewImageURL: aPreviewImage};
-              PlacesUtils.history.update(pageInfo).catch(Components.utils.reportError);
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="shouldLoadFavIcon">
-        <parameter name="aURI"/>
-        <body>
-          <![CDATA[
-            return (aURI &&
-                    Services.prefs.getBoolPref("browser.chrome.site_icons") &&
-                    Services.prefs.getBoolPref("browser.chrome.favicons") &&
-                    ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
-          ]]>
-        </body>
-      </method>
-
-      <method name="useDefaultIcon">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = this.getBrowserForTab(aTab);
-            let documentURI = browser.documentURI;
-            let requestContextID = browser.contentRequestContextID;
-            let loadingPrincipal = browser.contentPrincipal;
-            let icon = null;
-
-            if (browser.imageDocument) {
-              if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
-                let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
-                if (browser.imageDocument.width <= sz &&
-                    browser.imageDocument.height <= sz) {
-                  // Don't try to store the icon in Places, regardless it would
-                  // be skipped (see Bug 403651).
-                  icon = browser.currentURI;
-                }
-              }
-            }
-
-            // Use documentURIObject in the check for shouldLoadFavIcon so that we
-            // do the right thing with about:-style error pages.  Bug 453442
-            if (!icon && this.shouldLoadFavIcon(documentURI)) {
-              let url = documentURI.prePath + "/favicon.ico";
-              if (!this.isFailedIcon(url)) {
-                icon = url;
-                this.storeIcon(browser, icon, loadingPrincipal, requestContextID);
-              }
-            }
-
-            this.setIcon(aTab, icon, loadingPrincipal, requestContextID);
-          ]]>
-        </body>
-      </method>
-
-      <method name="isFailedIcon">
-        <parameter name="aURI"/>
-        <body>
-          <![CDATA[
-            if (!(aURI instanceof Ci.nsIURI))
-              aURI = makeURI(aURI);
-            return PlacesUtils.favicons.isFailedFavicon(aURI);
-          ]]>
-        </body>
-      </method>
-
-      <method name="getWindowTitleForBrowser">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            var newTitle = "";
-            var docElement = this.ownerDocument.documentElement;
-            var sep = docElement.getAttribute("titlemenuseparator");
-            let tab = this.getTabForBrowser(aBrowser);
-            let docTitle;
-
-            if (tab._labelIsContentTitle) {
-              // Strip out any null bytes in the content title, since the
-              // underlying widget implementations of nsWindow::SetTitle pass
-              // null-terminated strings to system APIs.
-              docTitle = tab.getAttribute("label").replace(/\0/g, "");
-            }
-
-            if (!docTitle)
-              docTitle = docElement.getAttribute("titledefault");
-
-            var modifier = docElement.getAttribute("titlemodifier");
-            if (docTitle) {
-              newTitle += docElement.getAttribute("titlepreface");
-              newTitle += docTitle;
-              if (modifier)
-                newTitle += sep;
-            }
-            newTitle += modifier;
-
-            // If location bar is hidden and the URL type supports a host,
-            // add the scheme and host to the title to prevent spoofing.
-            // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
-            try {
-              if (docElement.getAttribute("chromehidden").includes("location")) {
-                var uri = this.mURIFixup.createExposableURI(
-                            aBrowser.currentURI);
-                if (uri.scheme == "about")
-                  newTitle = uri.spec + sep + newTitle;
-                else
-                  newTitle = uri.prePath + sep + newTitle;
-              }
-            } catch (e) {}
-
-            return newTitle;
-          ]]>
-        </body>
-      </method>
-
-      <method name="updateTitlebar">
-        <body>
-          <![CDATA[
-            this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
-          ]]>
-        </body>
-      </method>
-
-      <!-- Holds a unique ID for the tab change that's currently being timed.
-           Used to make sure that multiple, rapid tab switches do not try to
-           create overlapping timers. -->
-      <field name="_tabSwitchID">null</field>
-
-      <method name="updateCurrentBrowser">
-        <parameter name="aForceUpdate"/>
-        <body>
-          <![CDATA[
-            var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
-            if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
-              return;
-
-            if (!aForceUpdate) {
-              document.commandDispatcher.lock();
-
-              TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
-              if (!gMultiProcessBrowser) {
-                // old way of measuring tab paint which is not valid with e10s.
-                // Waiting until the next MozAfterPaint ensures that we capture
-                // the time it takes to paint, upload the textures to the compositor,
-                // and then composite.
-                if (this._tabSwitchID) {
-                  TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_MS");
-                }
-
-                let tabSwitchID = Symbol();
-
-                TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_MS");
-                this._tabSwitchID = tabSwitchID;
-
-                let onMozAfterPaint = () => {
-                  if (this._tabSwitchID === tabSwitchID) {
-                    TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_MS");
-                    this._tabSwitchID = null;
-                  }
-                  window.removeEventListener("MozAfterPaint", onMozAfterPaint);
-                };
-                window.addEventListener("MozAfterPaint", onMozAfterPaint);
-              }
-            }
-
-            var oldTab = this.mCurrentTab;
-
-            // Preview mode should not reset the owner
-            if (!this._previewMode && !oldTab.selected)
-              oldTab.owner = null;
-
-            let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
-            if (lastRelatedTab) {
-              if (!lastRelatedTab.selected)
-                lastRelatedTab.owner = null;
-            }
-            this._lastRelatedTabMap = new WeakMap();
-
-            var oldBrowser = this.mCurrentBrowser;
-
-            if (!gMultiProcessBrowser) {
-              oldBrowser.removeAttribute("primary");
-              oldBrowser.docShellIsActive = false;
-              newBrowser.setAttribute("primary", "true");
-              newBrowser.docShellIsActive =
-                (window.windowState != window.STATE_MINIMIZED &&
-                 !window.isFullyOccluded);
-            }
-
-            var updateBlockedPopups = false;
-            if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
-                (!oldBrowser.blockedPopups && newBrowser.blockedPopups))
-              updateBlockedPopups = true;
-
-            this.mCurrentBrowser = newBrowser;
-            this.mCurrentTab = this.tabContainer.selectedItem;
-            this.showTab(this.mCurrentTab);
-
-            gURLBar.setAttribute("switchingtabs", "true");
-            window.addEventListener("MozAfterPaint", function() {
-              gURLBar.removeAttribute("switchingtabs");
-            }, {once: true});
-
-            this._appendStatusPanel();
-
-            if (updateBlockedPopups)
-              this.mCurrentBrowser.updateBlockedPopups();
-
-            // Update the URL bar.
-            var loc = this.mCurrentBrowser.currentURI;
-
-            var webProgress = this.mCurrentBrowser.webProgress;
-            var securityUI = this.mCurrentBrowser.securityUI;
-
-            this._callProgressListeners(null, "onLocationChange",
-                                        [webProgress, null, loc, 0], true,
-                                        false);
-
-            if (securityUI) {
-              // Include the true final argument to indicate that this event is
-              // simulated (instead of being observed by the webProgressListener).
-              this._callProgressListeners(null, "onSecurityChange",
-                                          [webProgress, null, securityUI.state, true],
-                                          true, false);
-            }
-
-            var listener = this._tabListeners.get(this.mCurrentTab);
-            if (listener && listener.mStateFlags) {
-              this._callProgressListeners(null, "onUpdateCurrentBrowser",
-                                          [listener.mStateFlags, listener.mStatus,
-                                           listener.mMessage, listener.mTotalProgress],
-                                          true, false);
-            }
-
-            if (!this._previewMode) {
-              this.mCurrentTab.updateLastAccessed();
-              this.mCurrentTab.removeAttribute("unread");
-              oldTab.updateLastAccessed();
-
-              let oldFindBar = oldTab._findBar;
-              if (oldFindBar &&
-                  oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
-                  !oldFindBar.hidden)
-                this._lastFindValue = oldFindBar._findField.value;
-
-              this.updateTitlebar();
-
-              this.mCurrentTab.removeAttribute("titlechanged");
-              this.mCurrentTab.removeAttribute("attention");
-
-              // The tab has been selected, it's not unselected anymore.
-              // (1) Call the current tab's finishUnselectedTabHoverTimer()
-              //     to save a telemetry record.
-              // (2) Call the current browser's unselectedTabHover() with false
-              //     to dispatch an event.
-              this.mCurrentTab.finishUnselectedTabHoverTimer();
-              this.mCurrentBrowser.unselectedTabHover(false);
-            }
-
-            // If the new tab is busy, and our current state is not busy, then
-            // we need to fire a start to all progress listeners.
-            const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
-            if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
-              this.mIsBusy = true;
-              this._callProgressListeners(null, "onStateChange",
-                                          [webProgress, null,
-                                           nsIWebProgressListener.STATE_START |
-                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
-                                          true, false);
-            }
-
-            // If the new tab is not busy, and our current state is busy, then
-            // we need to fire a stop to all progress listeners.
-            if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
-              this.mIsBusy = false;
-              this._callProgressListeners(null, "onStateChange",
-                                          [webProgress, null,
-                                           nsIWebProgressListener.STATE_STOP |
-                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
-                                          true, false);
-            }
-
-            // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
-            // that might rely upon the other changes suppressed.
-            // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
-            if (!this._previewMode) {
-              // We've selected the new tab, so go ahead and notify listeners.
-              let event = new CustomEvent("TabSelect", {
-                bubbles: true,
-                cancelable: false,
-                detail: {
-                  previousTab: oldTab
-                }
-              });
-              this.mCurrentTab.dispatchEvent(event);
-
-              this._tabAttrModified(oldTab, ["selected"]);
-              this._tabAttrModified(this.mCurrentTab, ["selected"]);
-
-              if (oldBrowser != newBrowser &&
-                  oldBrowser.getInPermitUnload) {
-                oldBrowser.getInPermitUnload(inPermitUnload => {
-                  if (!inPermitUnload) {
-                    return;
-                  }
-                  // Since the user is switching away from a tab that has
-                  // a beforeunload prompt active, we remove the prompt.
-                  // This prevents confusing user flows like the following:
-                  //   1. User attempts to close Firefox
-                  //   2. User switches tabs (ingoring a beforeunload prompt)
-                  //   3. User returns to tab, presses "Leave page"
-                  let promptBox = this.getTabModalPromptBox(oldBrowser);
-                  let prompts = promptBox.listPrompts();
-                  // There might not be any prompts here if the tab was closed
-                  // while in an onbeforeunload prompt, which will have
-                  // destroyed aforementioned prompt already, so check there's
-                  // something to remove, first:
-                  if (prompts.length) {
-                    // NB: This code assumes that the beforeunload prompt
-                    //     is the top-most prompt on the tab.
-                    prompts[prompts.length - 1].abortPrompt();
-                  }
-                });
-              }
-
-              if (!gMultiProcessBrowser) {
-                this._adjustFocusBeforeTabSwitch(oldTab, this.mCurrentTab);
-                this._adjustFocusAfterTabSwitch(this.mCurrentTab);
-              }
-            }
-
-            updateUserContextUIIndicator();
-            gIdentityHandler.updateSharingIndicator();
-
-            this.tabContainer._setPositionalAttributes();
-
-            // Enable touch events to start a native dragging
-            // session to allow the user to easily drag the selected tab.
-            // This is currently only supported on Windows.
-            oldTab.removeAttribute("touchdownstartsdrag");
-            this.mCurrentTab.setAttribute("touchdownstartsdrag", "true");
-
-            if (!gMultiProcessBrowser) {
-              document.commandDispatcher.unlock();
-
-              let event = new CustomEvent("TabSwitchDone", {
-                bubbles: true,
-                cancelable: true
-              });
-              this.dispatchEvent(event);
-            }
-
-            if (!aForceUpdate)
-              TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
-          ]]>
-        </body>
-      </method>
-
-      <method name="_adjustFocusBeforeTabSwitch">
-        <parameter name="oldTab"/>
-        <parameter name="newTab"/>
-        <body><![CDATA[
-          if (this._previewMode) {
-            return;
-          }
-
-          let oldBrowser = oldTab.linkedBrowser;
-          let newBrowser = newTab.linkedBrowser;
-
-          oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
-
-          if (this.isFindBarInitialized(oldTab)) {
-            let findBar = this.getFindBar(oldTab);
-            oldTab._findBarFocused = (!findBar.hidden &&
-              findBar._findField.getAttribute("focused") == "true");
-          }
-
-          let activeEl = document.activeElement;
-          // If focus is on the old tab, move it to the new tab.
-          if (activeEl == oldTab) {
-            newTab.focus();
-          } else if (gMultiProcessBrowser && activeEl != newBrowser && activeEl != newTab) {
-            // In e10s, if focus isn't already in the tabstrip or on the new browser,
-            // and the new browser's previous focus wasn't in the url bar but focus is
-            // there now, we need to adjust focus further.
-            let keepFocusOnUrlBar = newBrowser &&
-                                    newBrowser._urlbarFocused &&
-                                    gURLBar &&
-                                    gURLBar.focused;
-            if (!keepFocusOnUrlBar) {
-              // Clear focus so that _adjustFocusAfterTabSwitch can detect if
-              // some element has been focused and respect that.
-              document.activeElement.blur();
-            }
-          }
-        ]]></body>
-      </method>
-
-      <method name="_adjustFocusAfterTabSwitch">
-        <parameter name="newTab"/>
-        <body><![CDATA[
-        // Don't steal focus from the tab bar.
-        if (document.activeElement == newTab)
-          return;
-
-        let newBrowser = this.getBrowserForTab(newTab);
-
-        // If there's a tabmodal prompt showing, focus it.
-        if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
-          let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
-          let prompt = prompts[prompts.length - 1];
-          prompt.Dialog.setDefaultFocus();
-          return;
-        }
-
-        // Focus the location bar if it was previously focused for that tab.
-        // In full screen mode, only bother making the location bar visible
-        // if the tab is a blank one.
-        if (newBrowser._urlbarFocused && gURLBar) {
-          // Explicitly close the popup if the URL bar retains focus
-          gURLBar.closePopup();
-
-          // If the user happened to type into the URL bar for this browser
-          // by the time we got here, focusing will cause the text to be
-          // selected which could cause them to overwrite what they've
-          // already typed in.
-          if (gURLBar.focused && newBrowser.userTypedValue) {
-            return;
-          }
-
-          if (!window.fullScreen || isTabEmpty(newTab)) {
-            focusAndSelectUrlBar();
-            return;
-          }
-        }
-
-        // Focus the find bar if it was previously focused for that tab.
-        if (gFindBarInitialized && !gFindBar.hidden &&
-            this.selectedTab._findBarFocused) {
-          gFindBar._findField.focus();
-          return;
-        }
-
-        // Don't focus the content area if something has been focused after the
-        // tab switch was initiated.
-        if (gMultiProcessBrowser &&
-            document.activeElement != document.documentElement)
-          return;
-
-        // We're now committed to focusing the content area.
-        let fm = Services.focus;
-        let focusFlags = fm.FLAG_NOSCROLL;
-
-        if (!gMultiProcessBrowser) {
-          let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
-
-          // for anchors, use FLAG_SHOWRING so that it is clear what link was
-          // last clicked when switching back to that tab
-          if (newFocusedElement &&
-              (newFocusedElement instanceof HTMLAnchorElement ||
-               newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
-            focusFlags |= fm.FLAG_SHOWRING;
-        }
-
-        fm.setFocus(newBrowser, focusFlags);
-        ]]></body>
-      </method>
-
-      <method name="_tabAttrModified">
-        <parameter name="aTab"/>
-        <parameter name="aChanged"/>
-        <body><![CDATA[
-          if (aTab.closing)
-            return;
-
-          let event = new CustomEvent("TabAttrModified", {
-            bubbles: true,
-            cancelable: false,
-            detail: {
-              changed: aChanged,
-            }
-          });
-          aTab.dispatchEvent(event);
-        ]]></body>
-      </method>
-
-      <method name="setBrowserSharing">
-        <parameter name="aBrowser"/>
-        <parameter name="aState"/>
-        <body><![CDATA[
-          let tab = this.getTabForBrowser(aBrowser);
-          if (!tab)
-            return;
-
-          if (aState.sharing) {
-            tab._sharingState = aState;
-            if (aState.paused) {
-              tab.removeAttribute("sharing");
-            } else {
-              tab.setAttribute("sharing", aState.sharing);
-            }
-          } else {
-            tab._sharingState = null;
-            tab.removeAttribute("sharing");
-          }
-          this._tabAttrModified(tab, ["sharing"]);
-
-          if (aBrowser == this.mCurrentBrowser)
-            gIdentityHandler.updateSharingIndicator();
-        ]]></body>
-      </method>
-
-      <method name="getTabSharingState">
-        <parameter name="aTab"/>
-        <body><![CDATA[
-          // Normalize the state object for consumers (ie.extensions).
-          let state = Object.assign({}, aTab._sharingState);
-          return {
-            camera: !!state.camera,
-            microphone: !!state.microphone,
-            screen: state.screen && state.screen.replace("Paused", ""),
-          };
-        ]]></body>
-      </method>
-
-      <method name="setInitialTabTitle">
-        <parameter name="aTab"/>
-        <parameter name="aTitle"/>
-        <parameter name="aOptions"/>
-        <body><![CDATA[
-          if (aTitle) {
-            if (!aTab.getAttribute("label")) {
-              aTab._labelIsInitialTitle = true;
-            }
-
-            this._setTabLabel(aTab, aTitle, aOptions);
-          }
-        ]]></body>
-      </method>
-
-      <method name="setTabTitle">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            var browser = this.getBrowserForTab(aTab);
-            var title = browser.contentTitle;
-
-            // Don't replace an initially set label with the URL while the tab
-            // is loading.
-            if (aTab._labelIsInitialTitle) {
-              if (!title) {
-                return false;
-              }
-              delete aTab._labelIsInitialTitle;
-            }
-
-            let isContentTitle = false;
-            if (title) {
-              isContentTitle = true;
-            } else if (aTab.hasAttribute("customizemode")) {
-              let brandBundle = document.getElementById("bundle_brand");
-              let brandShortName = brandBundle.getString("brandShortName");
-              title = gNavigatorBundle.getFormattedString("customizeMode.tabTitle",
-                                                          [ brandShortName ]);
-              isContentTitle = true;
-            } else {
-              if (browser.currentURI.displaySpec) {
-                try {
-                  title = this.mURIFixup.createExposableURI(browser.currentURI).displaySpec;
-                } catch (ex) {
-                  title = browser.currentURI.displaySpec;
-                }
-              }
-
-              if (title && !isBlankPageURL(title)) {
-                // At this point, we now have a URI.
-                // Let's try to unescape it using a character set
-                // in case the URI is not ASCII.
-                try {
-                  // If it's a long data: URI that uses base64 encoding, truncate to
-                  // a reasonable length rather than trying to display the entire thing.
-                  // We can't shorten arbitrary URIs like this, as bidi etc might mean
-                  // we need the trailing characters for display. But a base64-encoded
-                  // data-URI is plain ASCII, so this is OK for tab-title display.
-                  // (See bug 1408854.)
-                  if (title.length > 500 && title.match(/^data:[^,]+;base64,/)) {
-                    title = title.substring(0, 500) + "\u2026";
-                  } else {
-                    var characterSet = browser.characterSet;
-                    title = Services.textToSubURI.unEscapeNonAsciiURI(characterSet, title);
-                  }
-                } catch (ex) { /* Do nothing. */ }
-              } else {
-                // Still no title? Fall back to our untitled string.
-                title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
-              }
-            }
-
-            return this._setTabLabel(aTab, title, { isContentTitle });
-          ]]>
-        </body>
-      </method>
-
-      <method name="_setTabLabel">
-        <parameter name="aTab"/>
-        <parameter name="aLabel"/>
-        <parameter name="aOptions"/>
-        <body>
-          <![CDATA[
-            if (!aLabel) {
-              return false;
-            }
-
-            aTab._fullLabel = aLabel;
-
-            aOptions = aOptions || {};
-            if (!aOptions.isContentTitle) {
-              // Remove protocol and "www."
-              if (!("_regex_shortenURLForTabLabel" in this)) {
-                this._regex_shortenURLForTabLabel = /^[^:]+:\/\/(?:www\.)?/;
-              }
-              aLabel = aLabel.replace(this._regex_shortenURLForTabLabel, "");
-            }
-
-            aTab._labelIsContentTitle = aOptions.isContentTitle;
-
-            if (aTab.getAttribute("label") == aLabel) {
-              return false;
-            }
-
-            let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindowUtils);
-            let isRTL = dwu.getDirectionFromText(aLabel) == Ci.nsIDOMWindowUtils.DIRECTION_RTL;
-
-            aTab.setAttribute("label", aLabel);
-            aTab.setAttribute("labeldirection", isRTL ? "rtl" : "ltr");
-
-            // Dispatch TabAttrModified event unless we're setting the label
-            // before the TabOpen event was dispatched.
-            if (!aOptions.beforeTabOpen) {
-              this._tabAttrModified(aTab, ["label"]);
-            }
-
-            if (aTab.selected) {
-              this.updateTitlebar();
-            }
-
-            return true;
-          ]]>
-        </body>
-      </method>
-
-      <method name="loadOneTab">
-        <parameter name="aURI"/>
-        <parameter name="aReferrerURI"/>
-        <parameter name="aCharset"/>
-        <parameter name="aPostData"/>
-        <parameter name="aLoadInBackground"/>
-        <parameter name="aAllowThirdPartyFixup"/>
-        <body>
-          <![CDATA[
-            var aTriggeringPrincipal;
-            var aReferrerPolicy;
-            var aFromExternal;
-            var aRelatedToCurrent;
-            var aAllowMixedContent;
-            var aSkipAnimation;
-            var aForceNotRemote;
-            var aPreferredRemoteType;
-            var aNoReferrer;
-            var aUserContextId;
-            var aSameProcessAsFrameLoader;
-            var aOriginPrincipal;
-            var aOpener;
-            var aOpenerBrowser;
-            var aCreateLazyBrowser;
-            var aNextTabParentId;
-            var aFocusUrlBar;
-            var aName;
-            if (arguments.length == 2 &&
-                typeof arguments[1] == "object" &&
-                !(arguments[1] instanceof Ci.nsIURI)) {
-              let params = arguments[1];
-              aTriggeringPrincipal      = params.triggeringPrincipal;
-              aReferrerURI              = params.referrerURI;
-              aReferrerPolicy           = params.referrerPolicy;
-              aCharset                  = params.charset;
-              aPostData                 = params.postData;
-              aLoadInBackground         = params.inBackground;
-              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
-              aFromExternal             = params.fromExternal;
-              aRelatedToCurrent         = params.relatedToCurrent;
-              aAllowMixedContent        = params.allowMixedContent;
-              aSkipAnimation            = params.skipAnimation;
-              aForceNotRemote           = params.forceNotRemote;
-              aPreferredRemoteType      = params.preferredRemoteType;
-              aNoReferrer               = params.noReferrer;
-              aUserContextId            = params.userContextId;
-              aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
-              aOriginPrincipal          = params.originPrincipal;
-              aOpener                   = params.opener;
-              aOpenerBrowser            = params.openerBrowser;
-              aCreateLazyBrowser        = params.createLazyBrowser;
-              aNextTabParentId          = params.nextTabParentId;
-              aFocusUrlBar              = params.focusUrlBar;
-              aName                     = params.name;
-            }
-
-            var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
-                         Services.prefs.getBoolPref("browser.tabs.loadInBackground");
-            var owner = bgLoad ? null : this.selectedTab;
-
-            var tab = this.addTab(aURI, {
-                                  triggeringPrincipal: aTriggeringPrincipal,
-                                  referrerURI: aReferrerURI,
-                                  referrerPolicy: aReferrerPolicy,
-                                  charset: aCharset,
-                                  postData: aPostData,
-                                  ownerTab: owner,
-                                  allowThirdPartyFixup: aAllowThirdPartyFixup,
-                                  fromExternal: aFromExternal,
-                                  relatedToCurrent: aRelatedToCurrent,
-                                  skipAnimation: aSkipAnimation,
-                                  allowMixedContent: aAllowMixedContent,
-                                  forceNotRemote: aForceNotRemote,
-                                  createLazyBrowser: aCreateLazyBrowser,
-                                  preferredRemoteType: aPreferredRemoteType,
-                                  noReferrer: aNoReferrer,
-                                  userContextId: aUserContextId,
-                                  originPrincipal: aOriginPrincipal,
-                                  sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
-                                  opener: aOpener,
-                                  openerBrowser: aOpenerBrowser,
-                                  nextTabParentId: aNextTabParentId,
-                                  focusUrlBar: aFocusUrlBar,
-                                  name: aName });
-            if (!bgLoad)
-              this.selectedTab = tab;
-
-            return tab;
-         ]]>
-        </body>
-      </method>
-
-      <method name="loadTabs">
-        <parameter name="aURIs"/>
-        <parameter name="aLoadInBackground"/>
-        <parameter name="aReplace"/>
-        <body><![CDATA[
-          let aTriggeringPrincipal;
-          let aAllowThirdPartyFixup;
-          let aTargetTab;
-          let aNewIndex = -1;
-          let aPostDatas = [];
-          let aUserContextId;
-          if (arguments.length == 2 &&
-              typeof arguments[1] == "object") {
-            let params = arguments[1];
-            aLoadInBackground     = params.inBackground;
-            aReplace              = params.replace;
-            aAllowThirdPartyFixup = params.allowThirdPartyFixup;
-            aTargetTab            = params.targetTab;
-            aNewIndex             = typeof params.newIndex === "number" ?
-                                    params.newIndex : aNewIndex;
-            aPostDatas            = params.postDatas || aPostDatas;
-            aUserContextId        = params.userContextId;
-            aTriggeringPrincipal  = params.triggeringPrincipal;
-          }
-
-          if (!aURIs.length)
-            return;
-
-          // The tab selected after this new tab is closed (i.e. the new tab's
-          // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
-          // when several urls are opened here (i.e. closing the first should select
-          // the next of many URLs opened) or if the pref to have UI links opened in
-          // the background is set (i.e. the link is not being opened modally)
-          //
-          // i.e.
-          //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
-          //    == 1              false                     YES
-          //    == 1              true                      NO
-          //    > 1               false/true                NO
-          var multiple = aURIs.length > 1;
-          var owner = multiple || aLoadInBackground ? null : this.selectedTab;
-          var firstTabAdded = null;
-          var targetTabIndex = -1;
-
-          if (aReplace) {
-            let browser;
-            if (aTargetTab) {
-              browser = this.getBrowserForTab(aTargetTab);
-              targetTabIndex = aTargetTab._tPos;
-            } else {
-              browser = this.mCurrentBrowser;
-              targetTabIndex = this.tabContainer.selectedIndex;
-            }
-            let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-            if (aAllowThirdPartyFixup) {
-              flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
-                       Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
-            }
-            try {
-              browser.loadURIWithFlags(aURIs[0], {
-                flags, postData: aPostDatas[0],
-                triggeringPrincipal: aTriggeringPrincipal,
-              });
-            } catch (e) {
-              // Ignore failure in case a URI is wrong, so we can continue
-              // opening the next ones.
-            }
-          } else {
-            firstTabAdded = this.addTab(aURIs[0], {
-              ownerTab: owner,
-              skipAnimation: multiple,
-              allowThirdPartyFixup: aAllowThirdPartyFixup,
-              postData: aPostDatas[0],
-              userContextId: aUserContextId,
-              triggeringPrincipal: aTriggeringPrincipal,
-            });
-            if (aNewIndex !== -1) {
-              this.moveTabTo(firstTabAdded, aNewIndex);
-              targetTabIndex = firstTabAdded._tPos;
-            }
-          }
-
-          let tabNum = targetTabIndex;
-          for (let i = 1; i < aURIs.length; ++i) {
-            let tab = this.addTab(aURIs[i], {
-              skipAnimation: true,
-              allowThirdPartyFixup: aAllowThirdPartyFixup,
-              postData: aPostDatas[i],
-              userContextId: aUserContextId,
-              triggeringPrincipal: aTriggeringPrincipal,
-            });
-            if (targetTabIndex !== -1)
-              this.moveTabTo(tab, ++tabNum);
-          }
-
-          if (firstTabAdded && !aLoadInBackground) {
-            this.selectedTab = firstTabAdded;
-          }
-        ]]></body>
-      </method>
-
-      <method name="updateBrowserRemoteness">
-        <parameter name="aBrowser"/>
-        <parameter name="aShouldBeRemote"/>
-        <parameter name="aOptions"/>
-        <body>
-          <![CDATA[
-            aOptions = aOptions || {};
-            let isRemote = aBrowser.getAttribute("remote") == "true";
-
-            if (!gMultiProcessBrowser && aShouldBeRemote) {
-              throw new Error("Cannot switch to remote browser in a window " +
-                              "without the remote tabs load context.");
-            }
-
-            // Default values for remoteType
-            if (!aOptions.remoteType) {
-              aOptions.remoteType = aShouldBeRemote ? E10SUtils.DEFAULT_REMOTE_TYPE : E10SUtils.NOT_REMOTE;
-            }
-
-            // If we are passed an opener, we must be making the browser non-remote, and
-            // if the browser is _currently_ non-remote, we need the openers to match,
-            // because it is already too late to change it.
-            if (aOptions.opener) {
-              if (aShouldBeRemote) {
-                throw new Error("Cannot set an opener on a browser which should be remote!");
-              }
-              if (!isRemote && aBrowser.contentWindow.opener != aOptions.opener) {
-                throw new Error("Cannot change opener on an already non-remote browser!");
-              }
-            }
-
-            // Abort if we're not going to change anything
-            let currentRemoteType = aBrowser.getAttribute("remoteType");
-            if (isRemote == aShouldBeRemote && !aOptions.newFrameloader &&
-                (!isRemote || currentRemoteType == aOptions.remoteType)) {
-              return false;
-            }
-
-            let tab = this.getTabForBrowser(aBrowser);
-            // aBrowser needs to be inserted now if it hasn't been already.
-            this._insertBrowser(tab);
-
-            let evt = document.createEvent("Events");
-            evt.initEvent("BeforeTabRemotenessChange", true, false);
-            tab.dispatchEvent(evt);
-
-            let wasActive = document.activeElement == aBrowser;
-
-            // Unmap the old outerWindowID.
-            this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
-
-            // Unhook our progress listener.
-            let filter = this._tabFilters.get(tab);
-            let listener = this._tabListeners.get(tab);
-            aBrowser.webProgress.removeProgressListener(filter);
-            filter.removeProgressListener(listener);
-
-            // We'll be creating a new listener, so destroy the old one.
-            listener.destroy();
-
-            let oldUserTypedValue = aBrowser.userTypedValue;
-            let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();
-
-            // Make sure the browser is destroyed so it unregisters from observer notifications
-            aBrowser.destroy();
-
-            // Make sure to restore the original droppedLinkHandler and
-            // sameProcessAsFrameLoader.
-            let droppedLinkHandler = aBrowser.droppedLinkHandler;
-            let sameProcessAsFrameLoader = aBrowser.sameProcessAsFrameLoader;
-
-            // Change the "remote" attribute.
-            let parent = aBrowser.parentNode;
-            parent.removeChild(aBrowser);
-            if (aShouldBeRemote) {
-              aBrowser.setAttribute("remote", "true");
-              aBrowser.setAttribute("remoteType", aOptions.remoteType);
-            } else {
-              aBrowser.setAttribute("remote", "false");
-              aBrowser.removeAttribute("remoteType");
-            }
-
-            // NB: This works with the hack in the browser constructor that
-            // turns this normal property into a field.
-            if (aOptions.sameProcessAsFrameLoader) {
-              // Always set sameProcessAsFrameLoader when passed in aOptions.
-              aBrowser.sameProcessAsFrameLoader = aOptions.sameProcessAsFrameLoader;
-            } else if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) {
-              // Only copy existing sameProcessAsFrameLoader when not switching
-              // remote type otherwise it would stop the switch.
-              aBrowser.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
-            }
-
-            if (aOptions.opener) {
-              // Set the opener window on the browser, such that when the frame
-              // loader is created the opener is set correctly.
-              aBrowser.presetOpenerWindow(aOptions.opener);
-            }
-
-            parent.appendChild(aBrowser);
-
-            aBrowser.userTypedValue = oldUserTypedValue;
-            if (hadStartedLoad) {
-              aBrowser.urlbarChangeTracker.startedLoad();
-            }
-
-            aBrowser.droppedLinkHandler = droppedLinkHandler;
-
-            // Switching a browser's remoteness will create a new frameLoader.
-            // As frameLoaders start out with an active docShell we have to
-            // deactivate it if this is not the selected tab's browser or the
-            // browser window is minimized.
-            aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);
-
-            // Create a new tab progress listener for the new browser we just injected,
-            // since tab progress listeners have logic for handling the initial about:blank
-            // load
-            listener = this.mTabProgressListener(tab, aBrowser, true, false);
-            this._tabListeners.set(tab, listener);
-            filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
-
-            // Restore the progress listener.
-            aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
-
-            // Restore the securityUI state.
-            let securityUI = aBrowser.securityUI;
-            let state = securityUI ? securityUI.state
-                                   : Ci.nsIWebProgressListener.STATE_IS_INSECURE;
-            // Include the true final argument to indicate that this event is
-            // simulated (instead of being observed by the webProgressListener).
-            this._callProgressListeners(aBrowser, "onSecurityChange",
-                                        [aBrowser.webProgress, null, state, true],
-                                        true, false);
-
-            if (aShouldBeRemote) {
-              // Switching the browser to be remote will connect to a new child
-              // process so the browser can no longer be considered to be
-              // crashed.
-              tab.removeAttribute("crashed");
-            } else {
-              aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });
-
-              // Register the new outerWindowID.
-              this._outerWindowIDBrowserMap.set(aBrowser.outerWindowID, aBrowser);
-            }
-
-            if (wasActive)
-              aBrowser.focus();
-
-            // If the findbar has been initialised, reset its browser reference.
-            if (this.isFindBarInitialized(tab)) {
-              this.getFindBar(tab).browser = aBrowser;
-            }
-
-            evt = document.createEvent("Events");
-            evt.initEvent("TabRemotenessChange", true, false);
-            tab.dispatchEvent(evt);
-
-            return true;
-          ]]>
-        </body>
-      </method>
-
-      <method name="updateBrowserRemotenessByURL">
-        <parameter name="aBrowser"/>
-        <parameter name="aURL"/>
-        <parameter name="aOptions"/>
-        <body>
-          <![CDATA[
-            aOptions = aOptions || {};
-
-            if (!gMultiProcessBrowser)
-              return this.updateBrowserRemoteness(aBrowser, false);
-
-            let currentRemoteType = aBrowser.getAttribute("remoteType") || null;
-
-            aOptions.remoteType =
-              E10SUtils.getRemoteTypeForURI(aURL,
-                                            gMultiProcessBrowser,
-                                            currentRemoteType,
-                                            aBrowser.currentURI);
-
-            // If this URL can't load in the current browser then flip it to the
-            // correct type.
-            if (currentRemoteType != aOptions.remoteType ||
-                aOptions.newFrameloader) {
-              let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE;
-              return this.updateBrowserRemoteness(aBrowser, remote, aOptions);
-            }
-
-            return false;
-          ]]>
-        </body>
-      </method>
-
-      <method name="removePreloadedBrowser">
-        <body>
-          <![CDATA[
-            if (!this._isPreloadingEnabled()) {
-              return;
-            }
-
-            let browser = this._getPreloadedBrowser();
-
-            if (browser) {
-              browser.remove();
-            }
-          ]]>
-        </body>
-      </method>
-
-      <field name="_preloadedBrowser">null</field>
-      <method name="_getPreloadedBrowser">
-        <body>
-          <![CDATA[
-            if (!this._isPreloadingEnabled()) {
-              return null;
-            }
-
-            // The preloaded browser might be null.
-            let browser = this._preloadedBrowser;
-
-            // Consume the browser.
-            this._preloadedBrowser = null;
-
-            // Attach the nsIFormFillController now that we know the browser
-            // will be used. If we do that before and the preloaded browser
-            // won't be consumed until shutdown then we leak a docShell.
-            // Also, we do not need to take care of attaching nsIFormFillControllers
-            // in the case that the browser is remote, as remote browsers take
-            // care of that themselves.
-            if (browser) {
-              browser.setAttribute("preloadedState", "consumed");
-              if (this.hasAttribute("autocompletepopup")) {
-                browser.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
-              }
-            }
-
-            return browser;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_isPreloadingEnabled">
-        <body>
-          <![CDATA[
-            // Preloading for the newtab page is enabled when the pref is true
-            // and the URL is "about:newtab". We do not support preloading for
-            // custom newtab URLs.
-            return Services.prefs.getBoolPref("browser.newtab.preload") &&
-                   !aboutNewTabService.overridden;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_createPreloadBrowser">
-        <body>
-          <![CDATA[
-            // Do nothing if we have a preloaded browser already
-            // or preloading of newtab pages is disabled.
-            if (this._preloadedBrowser || !this._isPreloadingEnabled()) {
-              return;
-            }
-
-            let remoteType =
-              E10SUtils.getRemoteTypeForURI(BROWSER_NEW_TAB_URL,
-                                            gMultiProcessBrowser);
-            let browser = this._createBrowser({isPreloadBrowser: true, remoteType});
-            this._preloadedBrowser = browser;
-
-            let notificationbox = this.getNotificationBox(browser);
-            this.mPanelContainer.appendChild(notificationbox);
-
-            if (remoteType != E10SUtils.NOT_REMOTE) {
-              // For remote browsers, we need to make sure that the webProgress is
-              // instantiated, otherwise the parent won't get informed about the state
-              // of the preloaded browser until it gets attached to a tab.
-              browser.webProgress;
-            }
-
-            browser.loadURI(BROWSER_NEW_TAB_URL);
-            browser.docShellIsActive = false;
-
-            // Make sure the preloaded browser is loaded with desired zoom level
-            let tabURI = Services.io.newURI(BROWSER_NEW_TAB_URL);
-            FullZoom.onLocationChange(tabURI, false, browser);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_createBrowser">
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            // Supported parameters:
-            // userContextId, remote, remoteType, isPreloadBrowser,
-            // uriIsAboutBlank, sameProcessAsFrameLoader
-
-            const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-            let b = document.createElementNS(NS_XUL, "browser");
-            b.permanentKey = {};
-            b.setAttribute("type", "content");
-            b.setAttribute("message", "true");
-            b.setAttribute("messagemanagergroup", "browsers");
-            b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
-            b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
-
-            if (aParams.userContextId) {
-              b.setAttribute("usercontextid", aParams.userContextId);
-            }
-
-            // remote parameter used by some addons, use default in this case.
-            if (aParams.remote && !aParams.remoteType) {
-              aParams.remoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
-            }
-
-            if (aParams.remoteType) {
-              b.setAttribute("remoteType", aParams.remoteType);
-              b.setAttribute("remote", "true");
-            }
-
-            if (aParams.openerWindow) {
-              if (aParams.remoteType) {
-                throw new Error("Cannot set opener window on a remote browser!");
-              }
-              b.presetOpenerWindow(aParams.openerWindow);
-            }
-
-            if (!aParams.isPreloadBrowser && this.hasAttribute("autocompletepopup")) {
-              b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
-            }
-
-            /*
-             * This attribute is meant to describe if the browser is the
-             * preloaded browser. There are 2 defined states: "preloaded" or
-             * "consumed". The order of events goes as follows:
-             *   1. The preloaded browser is created and the 'preloadedState'
-             *      attribute for that browser is set to "preloaded".
-             *   2. When a new tab is opened and it is time to show that
-             *      preloaded browser, the 'preloadedState' attribute for that
-             *      browser is set to "consumed"
-             *   3. When we then navigate away from about:newtab, the "consumed"
-             *      browsers will attempt to switch to a new content process,
-             *      therefore the 'preloadedState' attribute is removed from
-             *      that browser altogether
-             * See more details on Bug 1420285.
-             */
-            if (aParams.isPreloadBrowser) {
-              b.setAttribute("preloadedState", "preloaded");
-            }
-
-            if (this.hasAttribute("selectmenulist"))
-              b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
-
-            if (this.hasAttribute("datetimepicker")) {
-              b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
-            }
-
-            b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
-
-            if (aParams.nextTabParentId) {
-              if (!aParams.remoteType) {
-                throw new Error("Cannot have nextTabParentId without a remoteType");
-              }
-              // Gecko is going to read this attribute and use it.
-              b.setAttribute("nextTabParentId", aParams.nextTabParentId.toString());
-            }
-
-            if (aParams.sameProcessAsFrameLoader) {
-              b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
-            }
-
-            // This will be used by gecko to control the name of the opened
-            // window.
-            if (aParams.name) {
-              // XXX: The `name` property is special in HTML and XUL. Should
-              // we use a different attribute name for this?
-              b.setAttribute("name", aParams.name);
-            }
-
-            // Create the browserStack container
-            var stack = document.createElementNS(NS_XUL, "stack");
-            stack.className = "browserStack";
-            stack.appendChild(b);
-            stack.setAttribute("flex", "1");
-
-            // Create the browserContainer
-            var browserContainer = document.createElementNS(NS_XUL, "vbox");
-            browserContainer.className = "browserContainer";
-            browserContainer.appendChild(stack);
-            browserContainer.setAttribute("flex", "1");
-
-            // Create the sidebar container
-            var browserSidebarContainer = document.createElementNS(NS_XUL,
-                                                                   "hbox");
-            browserSidebarContainer.className = "browserSidebarContainer";
-            browserSidebarContainer.appendChild(browserContainer);
-            browserSidebarContainer.setAttribute("flex", "1");
-
-            // Add the Message and the Browser to the box
-            var notificationbox = document.createElementNS(NS_XUL,
-                                                           "notificationbox");
-            notificationbox.setAttribute("flex", "1");
-            notificationbox.setAttribute("notificationside", "top");
-            notificationbox.appendChild(browserSidebarContainer);
-
-            // Prevent the superfluous initial load of a blank document
-            // if we're going to load something other than about:blank.
-            if (!aParams.uriIsAboutBlank) {
-              b.setAttribute("nodefaultsrc", "true");
-            }
-
-            return b;
-          ]]>
-        </body>
-      </method>
-
-      <!--
-        `_createLazyBrowser` will define properties on the unbound lazy browser
-        which correspond to properties defined in XBL which will be bound to
-        the browser when it is inserted into the document.  If any of these
-        properties are accessed by consumers, `_insertBrowser` is called and
-        the browser is inserted to ensure that things don't break.  This list
-        provides the names of properties that may be called while the browser
-        is in its unbound (lazy) state.
-      -->
-      <field name="_browserBindingProperties">[
-        "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
-        "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
-        "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
-        "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
-        "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
-        "characterSet", "fullZoom", "textZoom", "webProgress",
-        "addProgressListener", "removeProgressListener", "audioPlaybackStarted",
-        "audioPlaybackStopped", "pauseMedia", "stopMedia",
-        "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
-        "purgeSessionHistory", "stopScroll", "startScroll",
-        "userTypedValue", "userTypedClear", "mediaBlocked",
-        "didStartLoadSinceLastUserTyping"
-      ]</field>
-
-      <method name="_createLazyBrowser">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = aTab.linkedBrowser;
-
-            let names = this._browserBindingProperties;
-
-            for (let i = 0; i < names.length; i++) {
-              let name = names[i];
-              let getter;
-              let setter;
-              switch (name) {
-                case "audioMuted":
-                  getter = () => false;
-                  break;
-                case "contentTitle":
-                  getter = () => SessionStore.getLazyTabValue(aTab, "title");
-                  break;
-                case "currentURI":
-                  getter = () => {
-                    let url = SessionStore.getLazyTabValue(aTab, "url");
-                    return Services.io.newURI(url);
-                  };
-                  break;
-                case "didStartLoadSinceLastUserTyping":
-                  getter = () => () => false;
-                  break;
-                case "fullZoom":
-                case "textZoom":
-                  getter = () => 1;
-                  break;
-                case "getTabBrowser":
-                  getter = () => () => this;
-                  break;
-                case "isRemoteBrowser":
-                  getter = () => browser.getAttribute("remote") == "true";
-                  break;
-                case "permitUnload":
-                  getter = () => () => ({ permitUnload: true, timedOut: false });
-                  break;
-                case "reload":
-                case "reloadWithFlags":
-                  getter = () =>
-                    params => {
-                      // Wait for load handler to be instantiated before
-                      // initializing the reload.
-                      aTab.addEventListener("SSTabRestoring", () => {
-                        browser[name](params);
-                      }, { once: true });
-                      gBrowser._insertBrowser(aTab);
-                    };
-                  break;
-                case "resumeMedia":
-                  getter = () =>
-                    () => {
-                      // No need to insert a browser, so we just call the browser's
-                      // method.
-                      aTab.addEventListener("SSTabRestoring", () => {
-                        browser[name]();
-                      }, { once: true });
-                    };
-                  break;
-                case "userTypedValue":
-                case "userTypedClear":
-                case "mediaBlocked":
-                  getter = () => SessionStore.getLazyTabValue(aTab, name);
-                  break;
-                default:
-                  getter = () => {
-                    if (AppConstants.NIGHTLY_BUILD) {
-                      let message =
-                        `[bug 1345098] Lazy browser prematurely inserted via '${name}' property access:\n`;
-                      console.log(message + new Error().stack);
-                    }
-                    this._insertBrowser(aTab);
-                    return browser[name];
-                  };
-                  setter = value => {
-                    if (AppConstants.NIGHTLY_BUILD) {
-                      let message =
-                        `[bug 1345098] Lazy browser prematurely inserted via '${name}' property access:\n`;
-                      console.log(message + new Error().stack);
-                    }
-                    this._insertBrowser(aTab);
-                    return browser[name] = value;
-                  };
-              }
-              Object.defineProperty(browser, name, {
-                get: getter,
-                set: setter,
-                configurable: true,
-                enumerable: true
-              });
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="_insertBrowser">
-        <parameter name="aTab"/>
-        <parameter name="aInsertedOnTabCreation"/>
-        <body>
-          <![CDATA[
-            "use strict";
-
-            // If browser is already inserted or window is closed don't do anything.
-            if (aTab.linkedPanel || window.closed) {
-              return;
-            }
-
-            let browser = aTab.linkedBrowser;
-
-            // If browser is a lazy browser, delete the substitute properties.
-            if (this._browserBindingProperties[0] in browser) {
-              for (let name of this._browserBindingProperties) {
-                delete browser[name];
-              }
-            }
-
-            let { uriIsAboutBlank, remoteType, usingPreloadedContent } =
-                    aTab._browserParams;
-            delete aTab._browserParams;
-
-            let notificationbox = this.getNotificationBox(browser);
-            let uniqueId = this._generateUniquePanelID();
-            notificationbox.id = uniqueId;
-            aTab.linkedPanel = uniqueId;
-
-            // Inject the <browser> into the DOM if necessary.
-            if (!notificationbox.parentNode) {
-              // NB: this appendChild call causes us to run constructors for the
-              // browser element, which fires off a bunch of notifications. Some
-              // of those notifications can cause code to run that inspects our
-              // state, so it is important that the tab element is fully
-              // initialized by this point.
-              this.mPanelContainer.appendChild(notificationbox);
-            }
-
-            // wire up a progress listener for the new browser object.
-            let tabListener = this.mTabProgressListener(aTab, browser, uriIsAboutBlank, usingPreloadedContent);
-            const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
-                                     .createInstance(Ci.nsIWebProgress);
-            filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
-            browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
-            this._tabListeners.set(aTab, tabListener);
-            this._tabFilters.set(aTab, filter);
-
-            browser.droppedLinkHandler = handleDroppedLink;
-
-            // We start our browsers out as inactive, and then maintain
-            // activeness in the tab switcher.
-            browser.docShellIsActive = false;
-
-            // When addTab() is called with an URL that is not "about:blank" we
-            // set the "nodefaultsrc" attribute that prevents a frameLoader
-            // from being created as soon as the linked <browser> is inserted
-            // into the DOM. We thus have to register the new outerWindowID
-            // for non-remote browsers after we have called browser.loadURI().
-            if (remoteType == E10SUtils.NOT_REMOTE) {
-              this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
-            }
-
-            var evt = new CustomEvent("TabBrowserInserted",
-              { bubbles: true, detail: { insertedOnTabCreation: aInsertedOnTabCreation } });
-            aTab.dispatchEvent(evt);
-          ]]>
-        </body>
-      </method>
-
-      <method name="discardBrowser">
-        <parameter name="aBrowser"/>
-        <parameter name="aForceDiscard"/>
-        <body>
-          <![CDATA[
-            "use strict";
-
-            let tab = this.getTabForBrowser(aBrowser);
-
-            let permitUnloadFlags = aForceDiscard ? aBrowser.dontPromptAndUnload : aBrowser.dontPromptAndDontUnload;
-
-            if (!tab ||
-                tab.selected ||
-                tab.closing ||
-                this._windowIsClosing ||
-                !aBrowser.isConnected ||
-                !aBrowser.isRemoteBrowser ||
-                !aBrowser.permitUnload(permitUnloadFlags).permitUnload) {
-              return;
-            }
-
-            // Set browser parameters for when browser is restored.  Also remove
-            // listeners and set up lazy restore data in SessionStore. This must
-            // be done before aBrowser is destroyed and removed from the document.
-            tab._browserParams = { uriIsAboutBlank: aBrowser.currentURI.spec == "about:blank",
-                                   remoteType: aBrowser.remoteType,
-                                   usingPreloadedContent: false };
-
-            SessionStore.resetBrowserToLazyState(tab);
-
-            this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
-
-            // Remove the tab's filter and progress listener.
-            let filter = this._tabFilters.get(tab);
-            let listener = this._tabListeners.get(tab);
-            aBrowser.webProgress.removeProgressListener(filter);
-            filter.removeProgressListener(listener);
-            listener.destroy();
-
-            this._tabListeners.delete(tab);
-            this._tabFilters.delete(tab);
-
-            // Reset the findbar and remove it if it is attached to the tab.
-            if (tab._findBar) {
-              tab._findBar.close(true);
-              tab._findBar.remove();
-              delete tab._findBar;
-            }
-
-            aBrowser.destroy();
-
-            let notificationbox = this.getNotificationBox(aBrowser);
-            this.mPanelContainer.removeChild(notificationbox);
-            tab.removeAttribute("linkedpanel");
-
-            this._createLazyBrowser(tab);
-
-            let evt = new CustomEvent("TabBrowserDiscarded", { bubbles: true });
-            tab.dispatchEvent(evt);
-          ]]>
-        </body>
-      </method>
-
-      <method name="addTab">
-        <parameter name="aURI"/>
-        <parameter name="aReferrerURI"/>
-        <parameter name="aCharset"/>
-        <parameter name="aPostData"/>
-        <parameter name="aOwner"/>
-        <parameter name="aAllowThirdPartyFixup"/>
-        <body>
-          <![CDATA[
-            "use strict";
-
-            const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-            var aTriggeringPrincipal;
-            var aReferrerPolicy;
-            var aFromExternal;
-            var aRelatedToCurrent;
-            var aSkipAnimation;
-            var aAllowMixedContent;
-            var aForceNotRemote;
-            var aPreferredRemoteType;
-            var aNoReferrer;
-            var aUserContextId;
-            var aEventDetail;
-            var aSameProcessAsFrameLoader;
-            var aOriginPrincipal;
-            var aDisallowInheritPrincipal;
-            var aOpener;
-            var aOpenerBrowser;
-            var aCreateLazyBrowser;
-            var aSkipBackgroundNotify;
-            var aNextTabParentId;
-            var aNoInitialLabel;
-            var aFocusUrlBar;
-            var aName;
-            if (arguments.length == 2 &&
-                typeof arguments[1] == "object" &&
-                !(arguments[1] instanceof Ci.nsIURI)) {
-              let params = arguments[1];
-              aTriggeringPrincipal      = params.triggeringPrincipal;
-              aReferrerURI              = params.referrerURI;
-              aReferrerPolicy           = params.referrerPolicy;
-              aCharset                  = params.charset;
-              aPostData                 = params.postData;
-              aOwner                    = params.ownerTab;
-              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
-              aFromExternal             = params.fromExternal;
-              aRelatedToCurrent         = params.relatedToCurrent;
-              aSkipAnimation            = params.skipAnimation;
-              aAllowMixedContent        = params.allowMixedContent;
-              aForceNotRemote           = params.forceNotRemote;
-              aPreferredRemoteType      = params.preferredRemoteType;
-              aNoReferrer               = params.noReferrer;
-              aUserContextId            = params.userContextId;
-              aEventDetail              = params.eventDetail;
-              aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
-              aOriginPrincipal          = params.originPrincipal;
-              aDisallowInheritPrincipal = params.disallowInheritPrincipal;
-              aOpener                   = params.opener;
-              aOpenerBrowser            = params.openerBrowser;
-              aCreateLazyBrowser        = params.createLazyBrowser;
-              aSkipBackgroundNotify     = params.skipBackgroundNotify;
-              aNextTabParentId          = params.nextTabParentId;
-              aNoInitialLabel           = params.noInitialLabel;
-              aFocusUrlBar              = params.focusUrlBar;
-              aName                     = params.name;
-            }
-
-            // if we're adding tabs, we're past interrupt mode, ditch the owner
-            if (this.mCurrentTab.owner)
-              this.mCurrentTab.owner = null;
-
-            // Find the tab that opened this one, if any. This is used for
-            // determining positioning, and inherited attributes such as the
-            // user context ID.
-            //
-            // If we have a browser opener (which is usually the browser
-            // element from a remote window.open() call), use that.
-            //
-            // Otherwise, if the tab is related to the current tab (e.g.,
-            // because it was opened by a link click), use the selected tab as
-            // the owner. If aReferrerURI is set, and we don't have an
-            // explicit relatedToCurrent arg, we assume that the tab is
-            // related to the current tab, since aReferrerURI is null or
-            // undefined if the tab is opened from an external application or
-            // bookmark (i.e. somewhere other than an existing tab).
-            let relatedToCurrent = aRelatedToCurrent == null ? !!aReferrerURI : aRelatedToCurrent;
-            let openerTab = ((aOpenerBrowser && this.getTabForBrowser(aOpenerBrowser)) ||
-                             (relatedToCurrent && this.selectedTab));
-
-            var t = document.createElementNS(NS_XUL, "tab");
-
-            t.openerTab = openerTab;
-
-            aURI = aURI || "about:blank";
-            let aURIObject = null;
-            try {
-              aURIObject = Services.io.newURI(aURI);
-            } catch (ex) { /* we'll try to fix up this URL later */ }
-
-            let lazyBrowserURI;
-            if (aCreateLazyBrowser && aURI != "about:blank") {
-              lazyBrowserURI = aURIObject;
-              aURI = "about:blank";
-            }
-
-            var uriIsAboutBlank = aURI == "about:blank";
-
-            if (!aNoInitialLabel) {
-              if (isBlankPageURL(aURI)) {
-                t.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"));
-              } else {
-                // Set URL as label so that the tab isn't empty initially.
-                this.setInitialTabTitle(t, aURI, { beforeTabOpen: true });
-              }
-            }
-
-            // Related tab inherits current tab's user context unless a different
-            // usercontextid is specified
-            if (aUserContextId == null && openerTab) {
-              aUserContextId = openerTab.getAttribute("usercontextid") || 0;
-            }
-
-            if (aUserContextId) {
-              t.setAttribute("usercontextid", aUserContextId);
-              ContextualIdentityService.setTabStyle(t);
-            }
-
-            t.setAttribute("onerror", "this.removeAttribute('image');");
-
-            if (aSkipBackgroundNotify) {
-              t.setAttribute("skipbackgroundnotify", true);
-            }
-
-            t.className = "tabbrowser-tab";
-
-            this.tabContainer._unlockTabSizing();
-
-            // When overflowing, new tabs are scrolled into view smoothly, which
-            // doesn't go well together with the width transition. So we skip the
-            // transition in that case.
-            let animate = !aSkipAnimation &&
-                          this.tabContainer.getAttribute("overflow") != "true" &&
-                          this.animationsEnabled;
-            if (!animate) {
-              t.setAttribute("fadein", "true");
-
-              // Call _handleNewTab asynchronously as it needs to know if the
-              // new tab is selected.
-              setTimeout(function(tabContainer) {
-                tabContainer._handleNewTab(t);
-              }, 0, this.tabContainer);
-            }
-
-            // invalidate cache
-            this._visibleTabs = null;
-
-            this.tabContainer.appendChild(t);
-
-            let usingPreloadedContent = false;
-            let b;
-
-            try {
-              // If this new tab is owned by another, assert that relationship
-              if (aOwner)
-                t.owner = aOwner;
-
-              var position = this.tabs.length - 1;
-              t._tPos = position;
-              this.tabContainer._setPositionalAttributes();
-
-              this.tabContainer.updateVisibility();
-
-              // If we don't have a preferred remote type, and we have a remote
-              // opener, use the opener's remote type.
-              if (!aPreferredRemoteType && aOpenerBrowser) {
-                aPreferredRemoteType = aOpenerBrowser.remoteType;
-              }
-
-              // If URI is about:blank and we don't have a preferred remote type,
-              // then we need to use the referrer, if we have one, to get the
-              // correct remote type for the new tab.
-              if (uriIsAboutBlank && !aPreferredRemoteType && aReferrerURI) {
-                aPreferredRemoteType =
-                  E10SUtils.getRemoteTypeForURI(aReferrerURI.spec,
-                                                gMultiProcessBrowser);
-              }
-
-              let remoteType =
-                aForceNotRemote ? E10SUtils.NOT_REMOTE
-                : E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser,
-                                                aPreferredRemoteType);
-
-              // If we open a new tab with the newtab URL in the default
-              // userContext, check if there is a preloaded browser ready.
-              // Private windows are not included because both the label and the
-              // icon for the tab would be set incorrectly (see bug 1195981).
-              if (aURI == BROWSER_NEW_TAB_URL &&
-                  !aUserContextId &&
-                  !PrivateBrowsingUtils.isWindowPrivate(window)) {
-                b = this._getPreloadedBrowser();
-                if (b) {
-                  usingPreloadedContent = true;
-                }
-              }
-
-              if (!b) {
-                // No preloaded browser found, create one.
-                b = this._createBrowser({ remoteType,
-                                          uriIsAboutBlank,
-                                          userContextId: aUserContextId,
-                                          sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
-                                          openerWindow: aOpener,
-                                          nextTabParentId: aNextTabParentId,
-                                          name: aName });
-              }
-
-              t.linkedBrowser = b;
-
-              if (aFocusUrlBar) {
-                b._urlbarFocused = true;
-              }
-
-              this._tabForBrowser.set(b, t);
-              t.permanentKey = b.permanentKey;
-              t._browserParams = { uriIsAboutBlank,
-                                   remoteType,
-                                   usingPreloadedContent };
-
-              // If the caller opts in, create a lazy browser.
-              if (aCreateLazyBrowser) {
-                this._createLazyBrowser(t);
-
-                if (lazyBrowserURI) {
-                  // Lazy browser must be explicitly registered so tab will appear as
-                  // a switch-to-tab candidate in autocomplete.
-                  this._unifiedComplete.registerOpenPage(lazyBrowserURI, aUserContextId);
-                  b.registeredOpenURI = lazyBrowserURI;
-                }
-              } else {
-                this._insertBrowser(t, true);
-              }
-            } catch (e) {
-              Cu.reportError("Failed to create tab");
-              Cu.reportError(e);
-              t.remove();
-              if (t.linkedBrowser) {
-                this._tabFilters.delete(t);
-                this._tabListeners.delete(t);
-                let notificationbox = this.getNotificationBox(t.linkedBrowser);
-                notificationbox.remove();
-              }
-              throw e;
-            }
-
-            // Hack to ensure that the about:newtab favicon is loaded
-            // instantaneously, to avoid flickering and improve perceived performance.
-            if (aURI == "about:newtab") {
-              this.setIcon(t, "chrome://branding/content/icon32.png");
-            } else if (aURI == "about:privatebrowsing") {
-              this.setIcon(t, "chrome://browser/skin/privatebrowsing/favicon.svg");
-            }
-
-            // Dispatch a new tab notification.  We do this once we're
-            // entirely done, so that things are in a consistent state
-            // even if the event listener opens or closes tabs.
-            var detail = aEventDetail || {};
-            var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
-            t.dispatchEvent(evt);
-
-            if (!usingPreloadedContent && aOriginPrincipal && aURI) {
-              let {URI_INHERITS_SECURITY_CONTEXT} = Ci.nsIProtocolHandler;
-              // Unless we know for sure we're not inheriting principals,
-              // force the about:blank viewer to have the right principal:
-              if (!aURIObject ||
-                  (doGetProtocolFlags(aURIObject) & URI_INHERITS_SECURITY_CONTEXT)) {
-                b.createAboutBlankContentViewer(aOriginPrincipal);
-              }
-            }
-
-            // If we didn't swap docShells with a preloaded browser
-            // then let's just continue loading the page normally.
-            if (!usingPreloadedContent && (!uriIsAboutBlank || aDisallowInheritPrincipal)) {
-              // pretend the user typed this so it'll be available till
-              // the document successfully loads
-              if (aURI && !gInitialPages.includes(aURI))
-                b.userTypedValue = aURI;
-
-              let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-              if (aAllowThirdPartyFixup) {
-                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
-                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
-              }
-              if (aFromExternal)
-                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
-              if (aAllowMixedContent)
-                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
-              if (aDisallowInheritPrincipal)
-                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
-              try {
-                b.loadURIWithFlags(aURI, {
-                  flags,
-                  triggeringPrincipal: aTriggeringPrincipal,
-                  referrerURI: aNoReferrer ? null : aReferrerURI,
-                  referrerPolicy: aReferrerPolicy,
-                  charset: aCharset,
-                  postData: aPostData,
-                });
-              } catch (ex) {
-                Cu.reportError(ex);
-              }
-            }
-
-            // If we're opening a tab related to the an existing tab, move it
-            // to a position after that tab.
-            if (openerTab &&
-                Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
-
-              let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
-              let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
-              if (lastRelatedTab)
-                lastRelatedTab.owner = null;
-              else
-                t.owner = openerTab;
-              this.moveTabTo(t, newTabPos, true);
-              this._lastRelatedTabMap.set(openerTab, t);
-            }
-
-            // This field is updated regardless if we actually animate
-            // since it's important that we keep this count correct in all cases.
-            this.tabAnimationsInProgress++;
-
-            if (animate) {
-              requestAnimationFrame(function() {
-                // kick the animation off
-                t.setAttribute("fadein", "true");
-              });
-            }
-
-            return t;
-          ]]>
-        </body>
-      </method>
-
-      <method name="warnAboutClosingTabs">
-      <parameter name="aCloseTabs"/>
-      <parameter name="aTab"/>
-      <body>
-        <![CDATA[
-          var tabsToClose;
-          switch (aCloseTabs) {
-            case this.closingTabsEnum.ALL:
-              tabsToClose = this.tabs.length - this._removingTabs.length -
-                            gBrowser._numPinnedTabs;
-              break;
-            case this.closingTabsEnum.OTHER:
-              tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
-              break;
-            case this.closingTabsEnum.TO_END:
-              if (!aTab)
-                throw new Error("Required argument missing: aTab");
-
-              tabsToClose = this.getTabsToTheEndFrom(aTab).length;
-              break;
-            default:
-              throw new Error("Invalid argument: " + aCloseTabs);
-          }
-
-          if (tabsToClose <= 1)
-            return true;
-
-          const pref = aCloseTabs == this.closingTabsEnum.ALL ?
-                       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
-          var shouldPrompt = Services.prefs.getBoolPref(pref);
-          if (!shouldPrompt)
-            return true;
-
-          var ps = Services.prompt;
-
-          // default to true: if it were false, we wouldn't get this far
-          var warnOnClose = { value: true };
-
-          // focus the window before prompting.
-          // this will raise any minimized window, which will
-          // make it obvious which window the prompt is for and will
-          // solve the problem of windows "obscuring" the prompt.
-          // see bug #350299 for more details
-          window.focus();
-          var warningMessage =
-            PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
-                      .replace("#1", tabsToClose);
-          var buttonPressed =
-            ps.confirmEx(window,
-                         gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
-                         warningMessage,
-                         (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
-                         + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
-                         gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
-                         null, null,
-                         aCloseTabs == this.closingTabsEnum.ALL ?
-                           gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
-                         warnOnClose);
-          var reallyClose = (buttonPressed == 0);
-
-          // don't set the pref unless they press OK and it's false
-          if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
-            Services.prefs.setBoolPref(pref, false);
-
-          return reallyClose;
-        ]]>
-      </body>
-      </method>
-
-      <method name="getTabsToTheEndFrom">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let tabsToEnd = [];
-            let tabs = this.visibleTabs;
-            for (let i = tabs.length - 1; i >= 0; --i) {
-              if (tabs[i] == aTab || tabs[i].pinned) {
-                break;
-              }
-              tabsToEnd.push(tabs[i]);
-            }
-            return tabsToEnd;
-          ]]>
-        </body>
-      </method>
-
-      <method name="removeTabsToTheEndFrom">
-        <parameter name="aTab"/>
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            if (!this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab))
-              return;
-
-            let removeTab = tab => {
-              // Avoid changing the selected browser several times.
-              if (tab.selected)
-                this.selectedTab = aTab;
-
-              this.removeTab(tab, aParams);
-            };
-
-            let tabs = this.getTabsToTheEndFrom(aTab);
-            let tabsWithBeforeUnload = [];
-            for (let i = tabs.length - 1; i >= 0; --i) {
-              let tab = tabs[i];
-              if (this._hasBeforeUnload(tab))
-                tabsWithBeforeUnload.push(tab);
-              else
-                removeTab(tab);
-            }
-            tabsWithBeforeUnload.forEach(removeTab);
-          ]]>
-        </body>
-      </method>
-
-      <method name="removeAllTabsBut">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
-              return;
-            }
-
-            let tabs = this.visibleTabs.reverse();
-            this.selectedTab = aTab;
-
-            let tabsWithBeforeUnload = [];
-            for (let i = tabs.length - 1; i >= 0; --i) {
-              let tab = tabs[i];
-              if (tab != aTab && !tab.pinned) {
-                if (this._hasBeforeUnload(tab))
-                  tabsWithBeforeUnload.push(tab);
-                else
-                  this.removeTab(tab, {animate: true});
-              }
-            }
-            for (let tab of tabsWithBeforeUnload) {
-              this.removeTab(tab, {animate: true});
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="removeCurrentTab">
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            this.removeTab(this.mCurrentTab, aParams);
-          ]]>
-        </body>
-      </method>
-
-      <field name="_removingTabs">
-        []
-      </field>
-
-      <method name="removeTab">
-        <parameter name="aTab"/>
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            if (aParams) {
-              var animate = aParams.animate;
-              var byMouse = aParams.byMouse;
-              var skipPermitUnload = aParams.skipPermitUnload;
-            }
-
-            // Telemetry stopwatches may already be running if removeTab gets
-            // called again for an already closing tab.
-            if (!TelemetryStopwatch.running("FX_TAB_CLOSE_TIME_ANIM_MS", aTab) &&
-                !TelemetryStopwatch.running("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab)) {
-              // Speculatevely start both stopwatches now. We'll cancel one of
-              // the two later depending on whether we're animating.
-              TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
-              TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
-            }
-            window.maybeRecordAbandonmentTelemetry(aTab, "tabClosed");
-
-            // Handle requests for synchronously removing an already
-            // asynchronously closing tab.
-            if (!animate &&
-                aTab.closing) {
-              this._endRemoveTab(aTab);
-              return;
-            }
-
-            var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
-
-            if (!this._beginRemoveTab(aTab, null, null, true, skipPermitUnload)) {
-              TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
-              TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
-              return;
-            }
-
-            if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
-              this.tabContainer._lockTabSizing(aTab);
-            else
-              this.tabContainer._unlockTabSizing();
-
-            if (!animate /* the caller didn't opt in */ ||
-                isLastTab ||
-                aTab.pinned ||
-                aTab.hidden ||
-                this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
-                aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
-                window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
-                !this.animationsEnabled) {
-              // We're not animating, so we can cancel the animation stopwatch.
-              TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_ANIM_MS", aTab);
-              this._endRemoveTab(aTab);
-              return;
-            }
-
-            // We're animating, so we can cancel the non-animation stopwatch.
-            TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
-
-            aTab.style.maxWidth = ""; // ensure that fade-out transition happens
-            aTab.removeAttribute("fadein");
-            aTab.removeAttribute("bursting");
-
-            setTimeout(function(tab, tabbrowser) {
-              if (tab.parentNode &&
-                  window.getComputedStyle(tab).maxWidth == "0.1px") {
-                NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
-                tabbrowser._endRemoveTab(tab);
-              }
-            }, 3000, aTab, this);
-          ]]>
-        </body>
-      </method>
-
-      <!-- Tab close requests are ignored if the window is closing anyway,
-           e.g. when holding Ctrl+W. -->
-      <field name="_windowIsClosing">
-        false
-      </field>
-
-      <method name="_hasBeforeUnload">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = aTab.linkedBrowser;
-            return browser.isRemoteBrowser && browser.frameLoader &&
-                   browser.frameLoader.tabParent &&
-                   browser.frameLoader.tabParent.hasBeforeUnload;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_beginRemoveTab">
-        <parameter name="aTab"/>
-        <parameter name="aAdoptedByTab"/>
-        <parameter name="aCloseWindowWithLastTab"/>
-        <parameter name="aCloseWindowFastpath"/>
-        <parameter name="aSkipPermitUnload"/>
-        <body>
-          <![CDATA[
-            if (aTab.closing ||
-                this._windowIsClosing)
-              return false;
-
-            var browser = this.getBrowserForTab(aTab);
-            if (!aSkipPermitUnload && !aAdoptedByTab &&
-                aTab.linkedPanel && !aTab._pendingPermitUnload &&
-                (!browser.isRemoteBrowser || this._hasBeforeUnload(aTab))) {
-              TelemetryStopwatch.start("FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS", aTab);
-
-              // We need to block while calling permitUnload() because it
-              // processes the event queue and may lead to another removeTab()
-              // call before permitUnload() returns.
-              aTab._pendingPermitUnload = true;
-              let {permitUnload, timedOut} = browser.permitUnload();
-              delete aTab._pendingPermitUnload;
-
-              TelemetryStopwatch.finish("FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS", aTab);
-
-              // If we were closed during onbeforeunload, we return false now
-              // so we don't (try to) close the same tab again. Of course, we
-              // also stop if the unload was cancelled by the user:
-              if (aTab.closing || (!timedOut && !permitUnload)) {
-                return false;
-              }
-            }
-
-            this._blurTab(aTab);
-
-            var closeWindow = false;
-            var newTab = false;
-            if (this.tabs.length - this._removingTabs.length == 1) {
-              closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
-                            !window.toolbar.visible ||
-                              Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
-
-              if (closeWindow) {
-                // We've already called beforeunload on all the relevant tabs if we get here,
-                // so avoid calling it again:
-                window.skipNextCanClose = true;
-              }
-
-              // Closing the tab and replacing it with a blank one is notably slower
-              // than closing the window right away. If the caller opts in, take
-              // the fast path.
-              if (closeWindow &&
-                  aCloseWindowFastpath &&
-                  this._removingTabs.length == 0) {
-                // This call actually closes the window, unless the user
-                // cancels the operation.  We are finished here in both cases.
-                this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
-                return false;
-              }
-
-              newTab = true;
-            }
-            aTab._endRemoveArgs = [closeWindow, newTab];
-
-            // swapBrowsersAndCloseOther will take care of closing the window without animation.
-            if (closeWindow && aAdoptedByTab) {
-              // Remove the tab's filter to avoid leaking.
-              if (aTab.linkedPanel) {
-                this._tabFilters.delete(aTab);
-              }
-              return true;
-            }
-
-            if (!aTab._fullyOpen) {
-              // If the opening tab animation hasn't finished before we start closing the
-              // tab, decrement the animation count since _handleNewTab will not get called.
-              this.tabAnimationsInProgress--;
-            }
-
-            this.tabAnimationsInProgress++;
-
-            // Mute audio immediately to improve perceived speed of tab closure.
-            if (!aAdoptedByTab && aTab.hasAttribute("soundplaying")) {
-              // Don't persist the muted state as this wasn't a user action.
-              // This lets undo-close-tab return it to an unmuted state.
-              aTab.linkedBrowser.mute(true);
-            }
-
-            aTab.closing = true;
-            this._removingTabs.push(aTab);
-            this._visibleTabs = null; // invalidate cache
-
-            // Invalidate hovered tab state tracking for this closing tab.
-            if (this.tabContainer._hoveredTab == aTab)
-              aTab._mouseleave();
-
-            if (newTab)
-              this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
-            else
-              this.tabContainer.updateVisibility();
-
-            // We're committed to closing the tab now.
-            // Dispatch a notification.
-            // We dispatch it before any teardown so that event listeners can
-            // inspect the tab that's about to close.
-            var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
-            aTab.dispatchEvent(evt);
-
-            if (aTab.linkedPanel) {
-              if (!aAdoptedByTab && !gMultiProcessBrowser) {
-                // Prevent this tab from showing further dialogs, since we're closing it
-                var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-                                  getInterface(Ci.nsIDOMWindowUtils);
-                windowUtils.disableDialogs();
-              }
-
-              // Remove the tab's filter and progress listener.
-              const filter = this._tabFilters.get(aTab);
-
-              browser.webProgress.removeProgressListener(filter);
-
-              const listener = this._tabListeners.get(aTab);
-              filter.removeProgressListener(listener);
-              listener.destroy();
-            }
-
-            if (browser.registeredOpenURI && !aAdoptedByTab) {
-              this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
-                                                       browser.getAttribute("usercontextid") || 0);
-              delete browser.registeredOpenURI;
-            }
-
-            // We are no longer the primary content area.
-            browser.removeAttribute("primary");
-
-            // Remove this tab as the owner of any other tabs, since it's going away.
-            for (let tab of this.tabs) {
-              if ("owner" in tab && tab.owner == aTab)
-                // |tab| is a child of the tab we're removing, make it an orphan
-                tab.owner = null;
-            }
-
-            return true;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_endRemoveTab">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            if (!aTab || !aTab._endRemoveArgs)
-              return;
-
-            var [aCloseWindow, aNewTab] = aTab._endRemoveArgs;
-            aTab._endRemoveArgs = null;
-
-            if (this._windowIsClosing) {
-              aCloseWindow = false;
-              aNewTab = false;
-            }
-
-            this.tabAnimationsInProgress--;
-
-            this._lastRelatedTabMap = new WeakMap();
-
-            // update the UI early for responsiveness
-            aTab.collapsed = true;
-            this._blurTab(aTab);
-
-            this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);
-
-            if (aCloseWindow) {
-              this._windowIsClosing = true;
-              while (this._removingTabs.length)
-                this._endRemoveTab(this._removingTabs[0]);
-            } else if (!this._windowIsClosing) {
-              if (aNewTab)
-                focusAndSelectUrlBar();
-
-              // workaround for bug 345399
-              this.tabContainer.arrowScrollbox._updateScrollButtonsDisabledState();
-            }
-
-            // We're going to remove the tab and the browser now.
-            this._tabFilters.delete(aTab);
-            this._tabListeners.delete(aTab);
-
-            var browser = this.getBrowserForTab(aTab);
-
-            if (aTab.linkedPanel) {
-              this._outerWindowIDBrowserMap.delete(browser.outerWindowID);
-
-              // Because of the way XBL works (fields just set JS
-              // properties on the element) and the code we have in place
-              // to preserve the JS objects for any elements that have
-              // JS properties set on them, the browser element won't be
-              // destroyed until the document goes away.  So we force a
-              // cleanup ourselves.
-              // This has to happen before we remove the child so that the
-              // XBL implementation of nsIObserver still works.
-              browser.destroy();
-            }
-
-            var wasPinned = aTab.pinned;
-
-            // Remove the tab ...
-            this.tabContainer.removeChild(aTab);
-
-            // ... and fix up the _tPos properties immediately.
-            for (let i = aTab._tPos; i < this.tabs.length; i++)
-              this.tabs[i]._tPos = i;
-
-            if (!this._windowIsClosing) {
-              if (wasPinned)
-                this.tabContainer._positionPinnedTabs();
-
-              // update tab close buttons state
-              this.tabContainer._updateCloseButtons();
-
-              setTimeout(function(tabs) {
-                tabs._lastTabClosedByMouse = false;
-              }, 0, this.tabContainer);
-            }
-
-            // update tab positional properties and attributes
-            this.selectedTab._selected = true;
-            this.tabContainer._setPositionalAttributes();
-
-            // Removing the panel requires fixing up selectedPanel immediately
-            // (see below), which would be hindered by the potentially expensive
-            // browser removal. So we remove the browser and the panel in two
-            // steps.
-
-            var panel = this.getNotificationBox(browser);
-
-            // In the multi-process case, it's possible an asynchronous tab switch
-            // is still underway. If so, then it's possible that the last visible
-            // browser is the one we're in the process of removing. There's the
-            // risk of displaying preloaded browsers that are at the end of the
-            // deck if we remove the browser before the switch is complete, so
-            // we alert the switcher in order to show a spinner instead.
-            if (this._switcher) {
-              this._switcher.onTabRemoved(aTab);
-            }
-
-            // This will unload the document. An unload handler could remove
-            // dependant tabs, so it's important that the tabbrowser is now in
-            // a consistent state (tab removed, tab positions updated, etc.).
-            browser.remove();
-
-            // Release the browser in case something is erroneously holding a
-            // reference to the tab after its removal.
-            this._tabForBrowser.delete(aTab.linkedBrowser);
-            aTab.linkedBrowser = null;
-
-            panel.remove();
-
-            // closeWindow might wait an arbitrary length of time if we're supposed
-            // to warn about closing the window, so we'll just stop the tab close
-            // stopwatches here instead.
-            TelemetryStopwatch.finish("FX_TAB_CLOSE_TIME_ANIM_MS", aTab,
-                                      true /* aCanceledOkay */);
-            TelemetryStopwatch.finish("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab,
-                                      true /* aCanceledOkay */);
-
-            if (aCloseWindow)
-              this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_findTabToBlurTo">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            if (!aTab.selected) {
-              return null;
-            }
-
-            if (aTab.owner &&
-                !aTab.owner.hidden &&
-                !aTab.owner.closing &&
-                Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
-              return aTab.owner;
-            }
-
-            // Switch to a visible tab unless there aren't any others remaining
-            let remainingTabs = this.visibleTabs;
-            let numTabs = remainingTabs.length;
-            if (numTabs == 0 || numTabs == 1 && remainingTabs[0] == aTab) {
-              remainingTabs = Array.filter(this.tabs, function(tab) {
-                return !tab.closing;
-              }, this);
-            }
-
-            // Try to find a remaining tab that comes after the given tab
-            let tab = aTab;
-            do {
-              tab = tab.nextSibling;
-            } while (tab && !remainingTabs.includes(tab));
-
-            if (!tab) {
-              tab = aTab;
-
-              do {
-                tab = tab.previousSibling;
-              } while (tab && !remainingTabs.includes(tab));
-            }
-
-            return tab;
-          ]]>
-        </body>
-      </method>
-
-      <method name="_blurTab">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            this.selectedTab = this._findTabToBlurTo(aTab);
-          ]]>
-        </body>
-      </method>
-
-      <method name="swapBrowsersAndCloseOther">
-        <parameter name="aOurTab"/>
-        <parameter name="aOtherTab"/>
-        <body>
-          <![CDATA[
-            // Do not allow transfering a private tab to a non-private window
-            // and vice versa.
-            if (PrivateBrowsingUtils.isWindowPrivate(window) !=
-                PrivateBrowsingUtils.isWindowPrivate(aOtherTab.ownerGlobal))
-              return;
-
-            let ourBrowser = this.getBrowserForTab(aOurTab);
-            let otherBrowser = aOtherTab.linkedBrowser;
-
-            // Can't swap between chrome and content processes.
-            if (ourBrowser.isRemoteBrowser != otherBrowser.isRemoteBrowser)
-              return;
-
-            // Keep the userContextId if set on other browser
-            if (otherBrowser.hasAttribute("usercontextid")) {
-              ourBrowser.setAttribute("usercontextid", otherBrowser.getAttribute("usercontextid"));
-            }
-
-            // That's gBrowser for the other window, not the tab's browser!
-            var remoteBrowser = aOtherTab.ownerGlobal.gBrowser;
-            var isPending = aOtherTab.hasAttribute("pending");
-
-            let otherTabListener = remoteBrowser._tabListeners.get(aOtherTab);
-            let stateFlags = otherTabListener.mStateFlags;
-
-            // Expedite the removal of the icon if it was already scheduled.
-            if (aOtherTab._soundPlayingAttrRemovalTimer) {
-              clearTimeout(aOtherTab._soundPlayingAttrRemovalTimer);
-              aOtherTab._soundPlayingAttrRemovalTimer = 0;
-              aOtherTab.removeAttribute("soundplaying");
-              remoteBrowser._tabAttrModified(aOtherTab, ["soundplaying"]);
-            }
-
-            // First, start teardown of the other browser.  Make sure to not
-            // fire the beforeunload event in the process.  Close the other
-            // window if this was its last tab.
-            if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true))
-              return;
-
-            // If this is the last tab of the window, hide the window
-            // immediately without animation before the docshell swap, to avoid
-            // about:blank being painted.
-            let [closeWindow] = aOtherTab._endRemoveArgs;
-            if (closeWindow) {
-              let win = aOtherTab.ownerGlobal;
-              let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindowUtils);
-              dwu.suppressAnimation(true);
-              // Only suppressing window animations isn't enough to avoid
-              // an empty content area being painted.
-              let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDocShell)
-                               .QueryInterface(Ci.nsIDocShellTreeItem)
-                               .treeOwner
-                               .QueryInterface(Ci.nsIBaseWindow);
-              baseWin.visibility = false;
-            }
-
-            let modifiedAttrs = [];
-            if (aOtherTab.hasAttribute("muted")) {
-              aOurTab.setAttribute("muted", "true");
-              aOurTab.muteReason = aOtherTab.muteReason;
-              ourBrowser.mute();
-              modifiedAttrs.push("muted");
-            }
-            if (aOtherTab.hasAttribute("soundplaying")) {
-              aOurTab.setAttribute("soundplaying", "true");
-              modifiedAttrs.push("soundplaying");
-            }
-            if (aOtherTab.hasAttribute("usercontextid")) {
-              aOurTab.setUserContextId(aOtherTab.getAttribute("usercontextid"));
-              modifiedAttrs.push("usercontextid");
-            }
-            if (aOtherTab.hasAttribute("sharing")) {
-              aOurTab.setAttribute("sharing", aOtherTab.getAttribute("sharing"));
-              modifiedAttrs.push("sharing");
-              aOurTab._sharingState = aOtherTab._sharingState;
-              webrtcUI.swapBrowserForNotification(otherBrowser, ourBrowser);
-            }
-
-            SitePermissions.copyTemporaryPermissions(otherBrowser, ourBrowser);
-
-            // If the other tab is pending (i.e. has not been restored, yet)
-            // then do not switch docShells but retrieve the other tab's state
-            // and apply it to our tab.
-            if (isPending) {
-              SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));
-
-              // Make sure to unregister any open URIs.
-              this._swapRegisteredOpenURIs(ourBrowser, otherBrowser);
-            } else {
-              // Workarounds for bug 458697
-              // Icon might have been set on DOMLinkAdded, don't override that.
-              if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
-                this.setIcon(aOurTab, otherBrowser.mIconURL, otherBrowser.contentPrincipal, otherBrowser.contentRequestContextID);
-              var isBusy = aOtherTab.hasAttribute("busy");
-              if (isBusy) {
-                aOurTab.setAttribute("busy", "true");
-                modifiedAttrs.push("busy");
-                if (aOurTab.selected)
-                  this.mIsBusy = true;
-              }
-
-              this._swapBrowserDocShells(aOurTab, otherBrowser, Ci.nsIBrowser.SWAP_DEFAULT, stateFlags);
-            }
-
-            // Unregister the previously opened URI
-            if (otherBrowser.registeredOpenURI) {
-              this._unifiedComplete.unregisterOpenPage(otherBrowser.registeredOpenURI,
-                                                       otherBrowser.getAttribute("usercontextid") || 0);
-              delete otherBrowser.registeredOpenURI;
-            }
-
-            // Handle findbar data (if any)
-            let otherFindBar = aOtherTab._findBar;
-            if (otherFindBar &&
-                otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
-              let ourFindBar = this.getFindBar(aOurTab);
-              ourFindBar._findField.value = otherFindBar._findField.value;
-              if (!otherFindBar.hidden)
-                ourFindBar.onFindCommand();
-            }
-
-            // Finish tearing down the tab that's going away.
-            if (closeWindow) {
-              aOtherTab.ownerGlobal.close();
-            } else {
-              remoteBrowser._endRemoveTab(aOtherTab);
-            }
-
-            this.setTabTitle(aOurTab);
-
-            // If the tab was already selected (this happpens in the scenario
-            // of replaceTabWithWindow), notify onLocationChange, etc.
-            if (aOurTab.selected)
-              this.updateCurrentBrowser(true);
-
-            if (modifiedAttrs.length) {
-              this._tabAttrModified(aOurTab, modifiedAttrs);
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="swapBrowsers">
-        <parameter name="aOurTab"/>
-        <parameter name="aOtherTab"/>
-        <parameter name="aFlags"/>
-        <body>
-          <![CDATA[
-            let otherBrowser = aOtherTab.linkedBrowser;
-            let otherTabBrowser = otherBrowser.getTabBrowser();
-
-            // We aren't closing the other tab so, we also need to swap its tablisteners.
-            let filter = otherTabBrowser._tabFilters.get(aOtherTab);
-            let tabListener = otherTabBrowser._tabListeners.get(aOtherTab);
-            otherBrowser.webProgress.removeProgressListener(filter);
-            filter.removeProgressListener(tabListener);
-
-            // Perform the docshell swap through the common mechanism.
-            this._swapBrowserDocShells(aOurTab, otherBrowser, aFlags);
-
-            // Restore the listeners for the swapped in tab.
-            tabListener = otherTabBrowser.mTabProgressListener(aOtherTab, otherBrowser, false, false);
-            otherTabBrowser._tabListeners.set(aOtherTab, tabListener);
-
-            const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
-            filter.addProgressListener(tabListener, notifyAll);
-            otherBrowser.webProgress.addProgressListener(filter, notifyAll);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_swapBrowserDocShells">
-        <parameter name="aOurTab"/>
-        <parameter name="aOtherBrowser"/>
-        <parameter name="aFlags"/>
-        <parameter name="aStateFlags"/>
-        <body>
-          <![CDATA[
-            // aOurTab's browser needs to be inserted now if it hasn't already.
-            this._insertBrowser(aOurTab);
-
-            // Unhook our progress listener
-            const filter = this._tabFilters.get(aOurTab);
-            let tabListener = this._tabListeners.get(aOurTab);
-            let ourBrowser = this.getBrowserForTab(aOurTab);
-            ourBrowser.webProgress.removeProgressListener(filter);
-            filter.removeProgressListener(tabListener);
-
-            // Make sure to unregister any open URIs.
-            this._swapRegisteredOpenURIs(ourBrowser, aOtherBrowser);
-
-            // Unmap old outerWindowIDs.
-            this._outerWindowIDBrowserMap.delete(ourBrowser.outerWindowID);
-            let remoteBrowser = aOtherBrowser.ownerGlobal.gBrowser;
-            if (remoteBrowser) {
-              remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
-            }
-
-            // If switcher is active, it will intercept swap events and
-            // react as needed.
-            if (!this._switcher) {
-              aOtherBrowser.docShellIsActive = this.shouldActivateDocShell(ourBrowser);
-            }
-
-            // Swap the docshells
-            ourBrowser.swapDocShells(aOtherBrowser);
-
-            if (ourBrowser.isRemoteBrowser) {
-              // Switch outerWindowIDs for remote browsers.
-              let ourOuterWindowID = ourBrowser._outerWindowID;
-              ourBrowser._outerWindowID = aOtherBrowser._outerWindowID;
-              aOtherBrowser._outerWindowID = ourOuterWindowID;
-            }
-
-            // Register new outerWindowIDs.
-            this._outerWindowIDBrowserMap.set(ourBrowser.outerWindowID, ourBrowser);
-            if (remoteBrowser) {
-              remoteBrowser._outerWindowIDBrowserMap.set(aOtherBrowser.outerWindowID, aOtherBrowser);
-            }
-
-            if (!(aFlags & Ci.nsIBrowser.SWAP_KEEP_PERMANENT_KEY)) {
-              // Swap permanentKey properties.
-              let ourPermanentKey = ourBrowser.permanentKey;
-              ourBrowser.permanentKey = aOtherBrowser.permanentKey;
-              aOtherBrowser.permanentKey = ourPermanentKey;
-              aOurTab.permanentKey = ourBrowser.permanentKey;
-              if (remoteBrowser) {
-                let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
-                if (otherTab) {
-                  otherTab.permanentKey = aOtherBrowser.permanentKey;
-                }
-              }
-            }
-
-            // Restore the progress listener
-            tabListener = this.mTabProgressListener(aOurTab, ourBrowser, false, false,
-                                                    aStateFlags);
-            this._tabListeners.set(aOurTab, tabListener);
-
-            const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
-            filter.addProgressListener(tabListener, notifyAll);
-            ourBrowser.webProgress.addProgressListener(filter, notifyAll);
-          ]]>
-        </body>
-      </method>
-
-      <method name="_swapRegisteredOpenURIs">
-        <parameter name="aOurBrowser"/>
-        <parameter name="aOtherBrowser"/>
-        <body>
-          <![CDATA[
-            // Swap the registeredOpenURI properties of the two browsers
-            let tmp = aOurBrowser.registeredOpenURI;
-            delete aOurBrowser.registeredOpenURI;
-            if (aOtherBrowser.registeredOpenURI) {
-              aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI;
-              delete aOtherBrowser.registeredOpenURI;
-            }
-            if (tmp) {
-              aOtherBrowser.registeredOpenURI = tmp;
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="reloadAllTabs">
-        <body>
-          <![CDATA[
-            let tabs = this.visibleTabs;
-            let l = tabs.length;
-            for (var i = 0; i < l; i++) {
-              try {
-                this.getBrowserForTab(tabs[i]).reload();
-              } catch (e) {
-                // ignore failure to reload so others will be reloaded
-              }
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="reloadTab">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = this.getBrowserForTab(aTab);
-            // Reset temporary permissions on the current tab. This is done here
-            // because we only want to reset permissions on user reload.
-            SitePermissions.clearTemporaryPermissions(browser);
-            browser.reload();
-          ]]>
-        </body>
-      </method>
-
-      <method name="addProgressListener">
-        <parameter name="aListener"/>
-        <body>
-          <![CDATA[
-            if (arguments.length != 1) {
-              Components.utils.reportError("gBrowser.addProgressListener was " +
-                                           "called with a second argument, " +
-                                           "which is not supported. See bug " +
-                                           "608628. Call stack: " + new Error().stack);
-            }
-
-            this.mProgressListeners.push(aListener);
-          ]]>
-        </body>
-      </method>
-
-      <method name="removeProgressListener">
-        <parameter name="aListener"/>
-        <body>
-          <![CDATA[
-            this.mProgressListeners =
-              this.mProgressListeners.filter(l => l != aListener);
-         ]]>
-        </body>
-      </method>
-
-      <method name="addTabsProgressListener">
-        <parameter name="aListener"/>
-        <body>
-          this.mTabsProgressListeners.push(aListener);
-        </body>
-      </method>
-
-      <method name="removeTabsProgressListener">
-        <parameter name="aListener"/>
-        <body>
-        <![CDATA[
-          this.mTabsProgressListeners =
-            this.mTabsProgressListeners.filter(l => l != aListener);
-        ]]>
-        </body>
-      </method>
-
-      <method name="getBrowserForTab">
-        <parameter name="aTab"/>
-        <body>
-        <![CDATA[
-          return aTab.linkedBrowser;
-        ]]>
-        </body>
-      </method>
-
-      <method name="showOnlyTheseTabs">
-        <parameter name="aTabs"/>
-        <body>
-        <![CDATA[
-          for (let tab of this.tabs) {
-            if (!aTabs.includes(tab))
-              this.hideTab(tab);
-            else
-              this.showTab(tab);
-          }
-
-          this.tabContainer._handleTabSelect(true);
-        ]]>
-        </body>
-      </method>
-
-      <method name="showTab">
-        <parameter name="aTab"/>
-        <body>
-        <![CDATA[
-          if (aTab.hidden) {
-            aTab.removeAttribute("hidden");
-            this._visibleTabs = null; // invalidate cache
-
-            this.tabContainer._updateCloseButtons();
-
-            this.tabContainer._setPositionalAttributes();
-
-            let event = document.createEvent("Events");
-            event.initEvent("TabShow", true, false);
-            aTab.dispatchEvent(event);
-            SessionStore.deleteTabValue(aTab, "hiddenBy");
-          }
-        ]]>
-        </body>
-      </method>
-
-      <method name="hideTab">
-        <parameter name="aTab"/>
-        <parameter name="aSource"/>
-        <body>
-        <![CDATA[
-          if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
-              !aTab.closing && !aTab._sharingState) {
-            aTab.setAttribute("hidden", "true");
-            this._visibleTabs = null; // invalidate cache
-
-            this.tabContainer._updateCloseButtons();
-
-            this.tabContainer._setPositionalAttributes();
-
-            let event = document.createEvent("Events");
-            event.initEvent("TabHide", true, false);
-            aTab.dispatchEvent(event);
-            if (aSource) {
-              SessionStore.setTabValue(aTab, "hiddenBy", aSource);
-            }
-          }
-        ]]>
-        </body>
-      </method>
-
-      <method name="selectTabAtIndex">
-        <parameter name="aIndex"/>
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          let tabs = this.visibleTabs;
-
-          // count backwards for aIndex < 0
-          if (aIndex < 0) {
-            aIndex += tabs.length;
-            // clamp at index 0 if still negative.
-            if (aIndex < 0)
-              aIndex = 0;
-          } else if (aIndex >= tabs.length) {
-            // clamp at right-most tab if out of range.
-            aIndex = tabs.length - 1;
-          }
-
-          this.selectedTab = tabs[aIndex];
-
-          if (aEvent) {
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-          }
-        ]]>
-        </body>
-      </method>
-
-      <property name="selectedTab">
-        <getter>
-          return this.mCurrentTab;
-        </getter>
-        <setter>
-          <![CDATA[
-          if (gNavToolbox.collapsed && !this._allowTabChange) {
-            return this.tabbox.selectedTab;
-          }
-          // Update the tab
-          this.tabbox.selectedTab = val;
-          return val;
-          ]]>
-        </setter>
-      </property>
-
-      <property name="selectedBrowser"
-                onget="return this.mCurrentBrowser;"
-                readonly="true"/>
-
-      <field name="browsers" readonly="true">
-        <![CDATA[
-          // This defines a proxy which allows us to access browsers by
-          // index without actually creating a full array of browsers.
-          new Proxy([], {
-            has: (target, name) => {
-              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
-                return (name in this.tabs);
-              }
-              return false;
-            },
-            get: (target, name) => {
-              if (name == "length") {
-                return this.tabs.length;
-              }
-              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
-                if (!(name in this.tabs)) {
-                  return undefined;
-                }
-                return this.tabs[name].linkedBrowser;
-              }
-              return target[name];
-            }
-          });
-        ]]>
-      </field>
-
-      <!-- Moves a tab to a new browser window, unless it's already the only tab
-           in the current window, in which case this will do nothing. -->
-      <method name="replaceTabWithWindow">
-        <parameter name="aTab"/>
-        <parameter name="aOptions"/>
-        <body>
-          <![CDATA[
-            if (this.tabs.length == 1)
-              return null;
-
-            var options = "chrome,dialog=no,all";
-            for (var name in aOptions)
-              options += "," + name + "=" + aOptions[name];
-
-            // Play the tab closing animation to give immediate feedback while
-            // waiting for the new window to appear.
-            // content area when the docshells are swapped.
-            if (this.animationsEnabled) {
-              aTab.style.maxWidth = ""; // ensure that fade-out transition happens
-              aTab.removeAttribute("fadein");
-            }
-
-            // tell a new window to take the "dropped" tab
-            return window.openDialog(getBrowserURL(), "_blank", options, aTab);
-          ]]>
-        </body>
-      </method>
-
-      <method name="moveTabTo">
-        <parameter name="aTab"/>
-        <parameter name="aIndex"/>
-        <parameter name="aKeepRelatedTabs"/>
-        <body>
-        <![CDATA[
-          var oldPosition = aTab._tPos;
-          if (oldPosition == aIndex)
-            return;
-
-          // Don't allow mixing pinned and unpinned tabs.
-          if (aTab.pinned)
-            aIndex = Math.min(aIndex, this._numPinnedTabs - 1);
-          else
-            aIndex = Math.max(aIndex, this._numPinnedTabs);
-          if (oldPosition == aIndex)
-            return;
-
-          if (!aKeepRelatedTabs) {
-            this._lastRelatedTabMap = new WeakMap();
-          }
-
-          let wasFocused = (document.activeElement == this.mCurrentTab);
-
-          aIndex = aIndex < aTab._tPos ? aIndex : aIndex + 1;
-
-          // invalidate cache
-          this._visibleTabs = null;
-
-          // use .item() instead of [] because dragging to the end of the strip goes out of
-          // bounds: .item() returns null (so it acts like appendChild), but [] throws
-          this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
-
-          for (let i = 0; i < this.tabs.length; i++) {
-            this.tabs[i]._tPos = i;
-            this.tabs[i]._selected = false;
-          }
-
-          // If we're in the midst of an async tab switch while calling
-          // moveTabTo, we can get into a case where _visuallySelected
-          // is set to true on two different tabs.
-          //
-          // What we want to do in moveTabTo is to remove logical selection
-          // from all tabs, and then re-add logical selection to mCurrentTab
-          // (and visual selection as well if we're not running with e10s, which
-          // setting _selected will do automatically).
-          //
-          // If we're running with e10s, then the visual selection will not
-          // be changed, which is fine, since if we weren't in the midst of a
-          // tab switch, the previously visually selected tab should still be
-          // correct, and if we are in the midst of a tab switch, then the async
-          // tab switcher will set the visually selected tab once the tab switch
-          // has completed.
-          this.mCurrentTab._selected = true;
-
-          if (wasFocused)
-            this.mCurrentTab.focus();
-
-          this.tabContainer._handleTabSelect(true);
-
-          if (aTab.pinned)
-            this.tabContainer._positionPinnedTabs();
-
-          this.tabContainer._setPositionalAttributes();
-
-          var evt = document.createEvent("UIEvents");
-          evt.initUIEvent("TabMove", true, false, window, oldPosition);
-          aTab.dispatchEvent(evt);
-        ]]>
-        </body>
-      </method>
-
-      <method name="moveTabForward">
-        <body>
-          <![CDATA[
-            let nextTab = this.mCurrentTab.nextSibling;
-            while (nextTab && nextTab.hidden)
-              nextTab = nextTab.nextSibling;
-
-            if (nextTab)
-              this.moveTabTo(this.mCurrentTab, nextTab._tPos);
-            else if (this.arrowKeysShouldWrap)
-              this.moveTabToStart();
-          ]]>
-        </body>
-      </method>
-
-      <!-- Adopts a tab from another browser window, and inserts it at aIndex -->
-      <method name="adoptTab">
-        <parameter name="aTab"/>
-        <parameter name="aIndex"/>
-        <parameter name="aSelectTab"/>
-        <body>
-        <![CDATA[
-          // Swap the dropped tab with a new one we create and then close
-          // it in the other window (making it seem to have moved between
-          // windows). We also ensure that the tab we create to swap into has
-          // the same remote type and process as the one we're swapping in.
-          // This makes sure we don't get a short-lived process for the new tab.
-          let linkedBrowser = aTab.linkedBrowser;
-          let params = { eventDetail: { adoptedTab: aTab },
-                         preferredRemoteType: linkedBrowser.remoteType,
-                         sameProcessAsFrameLoader: linkedBrowser.frameLoader,
-                         skipAnimation: true };
-          if (aTab.hasAttribute("usercontextid")) {
-            // new tab must have the same usercontextid as the old one
-            params.userContextId = aTab.getAttribute("usercontextid");
-          }
-          let newTab = this.addTab("about:blank", params);
-          let newBrowser = this.getBrowserForTab(newTab);
-
-          // Stop the about:blank load.
-          newBrowser.stop();
-          // Make sure it has a docshell.
-          newBrowser.docShell;
-
-          let numPinned = this._numPinnedTabs;
-          if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
-            this.pinTab(newTab);
-          }
-
-          this.moveTabTo(newTab, aIndex);
-
-          // We need to select the tab before calling swapBrowsersAndCloseOther
-          // so that window.content in chrome windows points to the right tab
-          // when pagehide/show events are fired. This is no longer necessary
-          // for any exiting browser code, but it may be necessary for add-on
-          // compatibility.
-          if (aSelectTab) {
-            this.selectedTab = newTab;
-          }
-
-          aTab.parentNode._finishAnimateTabMove();
-          this.swapBrowsersAndCloseOther(newTab, aTab);
-
-          if (aSelectTab) {
-            // Call updateCurrentBrowser to make sure the URL bar is up to date
-            // for our new tab after we've done swapBrowsersAndCloseOther.
-            this.updateCurrentBrowser(true);
-          }
-
-          return newTab;
-        ]]>
-        </body>
-      </method>
-
-
-      <method name="moveTabBackward">
-        <body>
-          <![CDATA[
-            let previousTab = this.mCurrentTab.previousSibling;
-            while (previousTab && previousTab.hidden)
-              previousTab = previousTab.previousSibling;
-
-            if (previousTab)
-              this.moveTabTo(this.mCurrentTab, previousTab._tPos);
-            else if (this.arrowKeysShouldWrap)
-              this.moveTabToEnd();
-          ]]>
-        </body>
-      </method>
-
-      <method name="moveTabToStart">
-        <body>
-          <![CDATA[
-            var tabPos = this.mCurrentTab._tPos;
-            if (tabPos > 0)
-              this.moveTabTo(this.mCurrentTab, 0);
-          ]]>
-        </body>
-      </method>
-
-      <method name="moveTabToEnd">
-        <body>
-          <![CDATA[
-            var tabPos = this.mCurrentTab._tPos;
-            if (tabPos < this.browsers.length - 1)
-              this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
-          ]]>
-        </body>
-      </method>
-
-      <method name="moveTabOver">
-        <parameter name="aEvent"/>
-        <body>
-          <![CDATA[
-            var direction = window.getComputedStyle(this.parentNode).direction;
-            if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
-                (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
-              this.moveTabForward();
-            else
-              this.moveTabBackward();
-          ]]>
-        </body>
-      </method>
-
-      <method name="duplicateTab">
-        <parameter name="aTab"/><!-- can be from a different window as well -->
-        <parameter name="aRestoreTabImmediately"/><!-- can defer loading of the tab contents -->
-        <body>
-          <![CDATA[
-            return SessionStore.duplicateTab(window, aTab, 0, aRestoreTabImmediately);
-          ]]>
-        </body>
-      </method>
-
-      <!--
-        List of browsers whose docshells must be active in order for print preview
-        to work.
-      -->
-      <field name="_printPreviewBrowsers">
-        new Set()
-      </field>
-
-      <method name="activateBrowserForPrintPreview">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            this._printPreviewBrowsers.add(aBrowser);
-            if (this._switcher) {
-              this._switcher.activateBrowserForPrintPreview(aBrowser);
-            }
-            aBrowser.docShellIsActive = true;
-          ]]>
-        </body>
-      </method>
-
-      <method name="deactivatePrintPreviewBrowsers">
-        <body>
-          <![CDATA[
-            let browsers = this._printPreviewBrowsers;
-            this._printPreviewBrowsers = new Set();
-            for (let browser of browsers) {
-              browser.docShellIsActive = this.shouldActivateDocShell(browser);
-            }
-          ]]>
-        </body>
-      </method>
-
-      <!--
-        Returns true if a given browser's docshell should be active.
-      -->
-      <method name="shouldActivateDocShell">
-        <parameter name="aBrowser"/>
-        <body>
-          <![CDATA[
-            if (this._switcher) {
-              return this._switcher.shouldActivateDocShell(aBrowser);
-            }
-            return (aBrowser == this.selectedBrowser &&
-                    window.windowState != window.STATE_MINIMIZED &&
-                    !window.isFullyOccluded) ||
-                   this._printPreviewBrowsers.has(aBrowser);
-          ]]>
-        </body>
-      </method>
-
-      <!--
-        The tab switcher is responsible for asynchronously switching
-        tabs in e10s. It waits until the new tab is ready (i.e., the
-        layer tree is available) before switching to it. Then it
-        unloads the layer tree for the old tab.
-
-        The tab switcher is a state machine. For each tab, it
-        maintains state about whether the layer tree for the tab is
-        available, being loaded, being unloaded, or unavailable. It
-        also keeps track of the tab currently being displayed, the tab
-        it's trying to load, and the tab the user has asked to switch
-        to. The switcher object is created upon tab switch. It is
-        released when there are no pending tabs to load or unload.
-
-        The following general principles have guided the design:
-
-        1. We only request one layer tree at a time. If the user
-        switches to a different tab while waiting, we don't request
-        the new layer tree until the old tab has loaded or timed out.
-
-        2. If loading the layers for a tab times out, we show the
-        spinner and possibly request the layer tree for another tab if
-        the user has requested one.
-
-        3. We discard layer trees on a delay. This way, if the user is
-        switching among the same tabs frequently, we don't continually
-        load the same tabs.
-
-        It's important that we always show either the spinner or a tab
-        whose layers are available. Otherwise the compositor will draw
-        an entirely black frame, which is very jarring. To ensure this
-        never happens when switching away from a tab, we assume the
-        old tab might still be drawn until a MozAfterPaint event
-        occurs. Because layout and compositing happen asynchronously,
-        we don't have any other way of knowing when the switch
-        actually takes place. Therefore, we don't unload the old tab
-        until the next MozAfterPaint event.
-      -->
-      <field name="_switcher">null</field>
-      <method name="_getSwitcher">
-        <body><![CDATA[
-          if (this._switcher) {
-            return this._switcher;
-          }
-
-          let switcher = {
-            // How long to wait for a tab's layers to load. After this
-            // time elapses, we're free to put up the spinner and start
-            // trying to load a different tab.
-            TAB_SWITCH_TIMEOUT: 400 /* ms */,
-
-            // When the user hasn't switched tabs for this long, we unload
-            // layers for all tabs that aren't in use.
-            UNLOAD_DELAY: 300 /* ms */,
-
-            // The next three tabs form the principal state variables.
-            // See the assertions in postActions for their invariants.
-
-            // Tab the user requested most recently.
-            requestedTab: this.selectedTab,
-
-            // Tab we're currently trying to load.
-            loadingTab: null,
-
-            // We show this tab in case the requestedTab hasn't loaded yet.
-            lastVisibleTab: this.selectedTab,
-
-            // Auxilliary state variables:
-
-            visibleTab: this.selectedTab,      // Tab that's on screen.
-            spinnerTab: null,                  // Tab showing a spinner.
-            blankTab: null,                    // Tab showing blank.
-            lastPrimaryTab: this.selectedTab,  // Tab with primary="true"
-
-            tabbrowser: this,  // Reference to gBrowser.
-            loadTimer: null,   // TAB_SWITCH_TIMEOUT nsITimer instance.
-            unloadTimer: null, // UNLOAD_DELAY nsITimer instance.
-
-            // Map from tabs to STATE_* (below).
-            tabState: new Map(),
-
-            // True if we're in the midst of switching tabs.
-            switchInProgress: false,
-
-            // Keep an exact list of content processes (tabParent) in which
-            // we're actively suppressing the display port. This gives a robust
-            // way to make sure we don't forget to un-suppress.
-            activeSuppressDisplayport: new Set(),
-
-            // Set of tabs that might be visible right now. We maintain
-            // this set because we can't be sure when a tab is actually
-            // drawn. A tab is added to this set when we ask to make it
-            // visible. All tabs but the most recently shown tab are
-            // removed from the set upon MozAfterPaint.
-            maybeVisibleTabs: new Set([this.selectedTab]),
-
-            // This holds onto the set of tabs that we've been asked to warm up.
-            // This is used only for Telemetry and logging, and (in order to not
-            // over-complicate the async tab switcher any further) has nothing to do
-            // with how warmed tabs are loaded and unloaded.
-            warmingTabs: new WeakSet(),
-
-            STATE_UNLOADED: 0,
-            STATE_LOADING: 1,
-            STATE_LOADED: 2,
-            STATE_UNLOADING: 3,
-
-            // re-entrancy guard:
-            _processing: false,
-
-            // Wraps nsITimer. Must not use the vanilla setTimeout and
-            // clearTimeout, because they will be blocked by nsIPromptService
-            // dialogs.
-            setTimer(callback, timeout) {
-              let event = {
-                notify: callback
-              };
-
-              var timer = Cc["@mozilla.org/timer;1"]
-                .createInstance(Components.interfaces.nsITimer);
-              timer.initWithCallback(event, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
-              return timer;
-            },
-
-            clearTimer(timer) {
-              timer.cancel();
-            },
-
-            getTabState(tab) {
-              let state = this.tabState.get(tab);
-
-              // As an optimization, we lazily evaluate the state of tabs
-              // that we've never seen before. Once we've figured it out,
-              // we stash it in our state map.
-              if (state === undefined) {
-                state = this.STATE_UNLOADED;
-
-                if (tab && tab.linkedPanel) {
-                  let b = tab.linkedBrowser;
-                  if (b.renderLayers && b.hasLayers) {
-                    state = this.STATE_LOADED;
-                  } else if (b.renderLayers && !b.hasLayers) {
-                    state = this.STATE_LOADING;
-                  } else if (!b.renderLayers && b.hasLayers) {
-                    state = this.STATE_UNLOADING;
-                  }
-                }
-
-                this.setTabStateNoAction(tab, state);
-              }
-
-              return state;
-            },
-
-            setTabStateNoAction(tab, state) {
-              if (state == this.STATE_UNLOADED) {
-                this.tabState.delete(tab);
-              } else {
-                this.tabState.set(tab, state);
-              }
-            },
-
-            setTabState(tab, state) {
-              if (state == this.getTabState(tab)) {
-                return;
-              }
-
-              this.setTabStateNoAction(tab, state);
-
-              let browser = tab.linkedBrowser;
-              let {tabParent} = browser.frameLoader;
-              if (state == this.STATE_LOADING) {
-                this.assert(!this.minimizedOrFullyOccluded);
-
-                if (!this.tabbrowser.tabWarmingEnabled) {
-                  browser.docShellIsActive = true;
-                }
-
-                if (tabParent) {
-                  browser.renderLayers = true;
-                } else {
-                  this.onLayersReady(browser);
-                }
-              } else if (state == this.STATE_UNLOADING) {
-                this.unwarmTab(tab);
-                // Setting the docShell to be inactive will also cause it
-                // to stop rendering layers.
-                browser.docShellIsActive = false;
-                if (!tabParent) {
-                  this.onLayersCleared(browser);
-                }
-              } else if (state == this.STATE_LOADED) {
-                this.maybeActivateDocShell(tab);
-              }
-
-              if (!tab.linkedBrowser.isRemoteBrowser) {
-                // setTabState is potentially re-entrant in the non-remote case,
-                // so we must re-get the state for this assertion.
-                let nonRemoteState = this.getTabState(tab);
-                // Non-remote tabs can never stay in the STATE_LOADING
-                // or STATE_UNLOADING states. By the time this function
-                // exits, a non-remote tab must be in STATE_LOADED or
-                // STATE_UNLOADED, since the painting and the layer
-                // upload happen synchronously.
-                this.assert(nonRemoteState == this.STATE_UNLOADED ||
-                            nonRemoteState == this.STATE_LOADED);
-              }
-            },
-
-            get minimizedOrFullyOccluded() {
-              return window.windowState == window.STATE_MINIMIZED ||
-                     window.isFullyOccluded;
-            },
-
-            init() {
-              this.log("START");
-
-              window.addEventListener("MozAfterPaint", this);
-              window.addEventListener("MozLayerTreeReady", this);
-              window.addEventListener("MozLayerTreeCleared", this);
-              window.addEventListener("TabRemotenessChange", this);
-              window.addEventListener("sizemodechange", this);
-              window.addEventListener("occlusionstatechange", this);
-              window.addEventListener("SwapDocShells", this, true);
-              window.addEventListener("EndSwapDocShells", this, true);
-
-              let initialTab = this.requestedTab;
-              let initialBrowser = initialTab.linkedBrowser;
-
-              let tabIsLoaded = !initialBrowser.isRemoteBrowser ||
-                                initialBrowser.frameLoader.tabParent.hasLayers;
-
-              // If we minimized the window before the switcher was activated,
-              // we might have set  the preserveLayers flag for the current
-              // browser. Let's clear it.
-              initialBrowser.preserveLayers(false);
-
-              if (!this.minimizedOrFullyOccluded) {
-                this.log("Initial tab is loaded?: " + tabIsLoaded);
-                this.setTabState(initialTab, tabIsLoaded ? this.STATE_LOADED
-                                                         : this.STATE_LOADING);
-              }
-
-              for (let ppBrowser of this.tabbrowser._printPreviewBrowsers) {
-                let ppTab = this.tabbrowser.getTabForBrowser(ppBrowser);
-                let state = ppBrowser.hasLayers ? this.STATE_LOADED
-                                                : this.STATE_LOADING;
-                this.setTabState(ppTab, state);
-              }
-            },
-
-            destroy() {
-              if (this.unloadTimer) {
-                this.clearTimer(this.unloadTimer);
-                this.unloadTimer = null;
-              }
-              if (this.loadTimer) {
-                this.clearTimer(this.loadTimer);
-                this.loadTimer = null;
-              }
-
-              window.removeEventListener("MozAfterPaint", this);
-              window.removeEventListener("MozLayerTreeReady", this);
-              window.removeEventListener("MozLayerTreeCleared", this);
-              window.removeEventListener("TabRemotenessChange", this);
-              window.removeEventListener("sizemodechange", this);
-              window.removeEventListener("occlusionstatechange", this);
-              window.removeEventListener("SwapDocShells", this, true);
-              window.removeEventListener("EndSwapDocShells", this, true);
-
-              this.tabbrowser._switcher = null;
-
-              this.activeSuppressDisplayport.forEach(function(tabParent) {
-                tabParent.suppressDisplayport(false);
-              });
-              this.activeSuppressDisplayport.clear();
-            },
-
-            finish() {
-              this.log("FINISH");
-
-              this.assert(this.tabbrowser._switcher);
-              this.assert(this.tabbrowser._switcher === this);
-              this.assert(!this.spinnerTab);
-              this.assert(!this.blankTab);
-              this.assert(!this.loadTimer);
-              this.assert(!this.loadingTab);
-              this.assert(this.lastVisibleTab === this.requestedTab);
-              this.assert(this.minimizedOrFullyOccluded ||
-                          this.getTabState(this.requestedTab) == this.STATE_LOADED);
-
-              this.destroy();
-
-              document.commandDispatcher.unlock();
-
-              let event = new CustomEvent("TabSwitchDone", {
-                bubbles: true,
-                cancelable: true
-              });
-              this.tabbrowser.dispatchEvent(event);
-            },
-
-            // This function is called after all the main state changes to
-            // make sure we display the right tab.
-            updateDisplay() {
-              let requestedTabState = this.getTabState(this.requestedTab);
-              let requestedBrowser = this.requestedTab.linkedBrowser;
-
-              // It is often more desirable to show a blank tab when appropriate than
-              // the tab switch spinner - especially since the spinner is usually
-              // preceded by a perceived lag of TAB_SWITCH_TIMEOUT ms in the
-              // tab switch. We can hide this lag, and hide the time being spent
-              // constructing TabChild's, layer trees, etc, by showing a blank
-              // tab instead and focusing it immediately.
-              let shouldBeBlank = false;
-              if (requestedBrowser.isRemoteBrowser) {
-                // If a tab is remote and the window is not minimized, we can show a
-                // blank tab instead of a spinner in the following cases:
-                //
-                // 1. The tab has just crashed, and we haven't started showing the
-                //    tab crashed page yet (in this case, the TabParent is null)
-                // 2. The tab has never presented, and has not finished loading
-                //    a non-local-about: page.
-                //
-                // For (2), "finished loading a non-local-about: page" is
-                // determined by the busy state on the tab element and checking
-                // if the loaded URI is local.
-                let hasSufficientlyLoaded =
-                  !this.requestedTab.hasAttribute("busy") &&
-                  !this.tabbrowser._isLocalAboutURI(requestedBrowser.currentURI);
-
-                let fl = requestedBrowser.frameLoader;
-                shouldBeBlank = !this.minimizedOrFullyOccluded &&
-                                (!fl.tabParent ||
-                                 (!hasSufficientlyLoaded && !fl.tabParent.hasPresented));
-              }
-
-              this.log("Tab should be blank: " + shouldBeBlank);
-              this.log("Requested tab is remote?: " + requestedBrowser.isRemoteBrowser);
-
-              // Figure out which tab we actually want visible right now.
-              let showTab = null;
-              if (requestedTabState != this.STATE_LOADED &&
-                  this.lastVisibleTab && this.loadTimer && !shouldBeBlank) {
-                // If we can't show the requestedTab, and lastVisibleTab is
-                // available, show it.
-                showTab = this.lastVisibleTab;
-              } else {
-                // Show the requested tab. If it's not available, we'll show the spinner or a blank tab.
-                showTab = this.requestedTab;
-              }
-
-              // First, let's deal with blank tabs, which we show instead
-              // of the spinner when the tab is not currently set up
-              // properly in the content process.
-              if (!shouldBeBlank && this.blankTab) {
-                this.blankTab.linkedBrowser.removeAttribute("blank");
-                this.blankTab = null;
-              } else if (shouldBeBlank && this.blankTab !== showTab) {
-                if (this.blankTab) {
-                  this.blankTab.linkedBrowser.removeAttribute("blank");
-                }
-                this.blankTab = showTab;
-                this.blankTab.linkedBrowser.setAttribute("blank", "true");
-              }
-
-              // Show or hide the spinner as needed.
-              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED &&
-                                !this.minimizedOrFullyOccluded &&
-                                !shouldBeBlank;
-
-              if (!needSpinner && this.spinnerTab) {
-                this.spinnerHidden();
-                this.tabbrowser.removeAttribute("pendingpaint");
-                this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
-                this.spinnerTab = null;
-              } else if (needSpinner && this.spinnerTab !== showTab) {
-                if (this.spinnerTab) {
-                  this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
-                } else {
-                  this.spinnerDisplayed();
-                }
-                this.spinnerTab = showTab;
-                this.tabbrowser.setAttribute("pendingpaint", "true");
-                this.spinnerTab.linkedBrowser.setAttribute("pendingpaint", "true");
-              }
-
-              // Switch to the tab we've decided to make visible.
-              if (this.visibleTab !== showTab) {
-                this.tabbrowser._adjustFocusBeforeTabSwitch(this.visibleTab, showTab);
-                this.visibleTab = showTab;
-
-                this.maybeVisibleTabs.add(showTab);
-
-                let tabs = this.tabbrowser.tabbox.tabs;
-                let tabPanel = this.tabbrowser.mPanelContainer;
-                let showPanel = tabs.getRelatedElement(showTab);
-                let index = Array.indexOf(tabPanel.childNodes, showPanel);
-                if (index != -1) {
-                  this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
-                  tabPanel.setAttribute("selectedIndex", index);
-                  if (showTab === this.requestedTab) {
-                    if (this._requestingTab) {
-                      /*
-                       * If _requestingTab is set, that means that we're switching the
-                       * visibility of the tab synchronously, and we need to wait for
-                       * the "select" event before shifting focus so that
-                       * _adjustFocusAfterTabSwitch runs with the right information for
-                       * the tab switch.
-                       */
-                      this.tabbrowser.addEventListener("select", () => {
-                        this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
-                      }, {once: true});
-                    } else {
-                      this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
-                    }
-
-                    this.maybeActivateDocShell(this.requestedTab);
-                  }
-                }
-
-                // This doesn't necessarily exist if we're a new window and haven't switched tabs yet
-                if (this.lastVisibleTab)
-                  this.lastVisibleTab._visuallySelected = false;
-
-                this.visibleTab._visuallySelected = true;
-              }
-
-              this.lastVisibleTab = this.visibleTab;
-            },
-
-            assert(cond) {
-              if (!cond) {
-                dump("Assertion failure\n" + Error().stack);
-
-                // Don't break a user's browser if an assertion fails.
-                if (AppConstants.DEBUG) {
-                  throw new Error("Assertion failure");
-                }
-              }
-            },
-
-            // We've decided to try to load requestedTab.
-            loadRequestedTab() {
-              this.assert(!this.loadTimer);
-              this.assert(!this.minimizedOrFullyOccluded);
-
-              // loadingTab can be non-null here if we timed out loading the current tab.
-              // In that case we just overwrite it with a different tab; it's had its chance.
-              this.loadingTab = this.requestedTab;
-              this.log("Loading tab " + this.tinfo(this.loadingTab));
-
-              this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
-              this.setTabState(this.requestedTab, this.STATE_LOADING);
-            },
-
-            maybeActivateDocShell(tab) {
-              // If we've reached the point where the requested tab has entered
-              // the loaded state, but the DocShell is still not yet active, we
-              // should activate it.
-              let browser = tab.linkedBrowser;
-              let state = this.getTabState(tab);
-              let canCheckDocShellState = !browser.mDestroyed &&
-                                          (browser.docShell ||
-                                           browser.frameLoader.tabParent);
-              if (tab == this.requestedTab &&
-                  canCheckDocShellState &&
-                  state == this.STATE_LOADED &&
-                  !browser.docShellIsActive &&
-                  !this.minimizedOrFullyOccluded) {
-                browser.docShellIsActive = true;
-                this.logState("Set requested tab docshell to active and preserveLayers to false");
-                // If we minimized the window before the switcher was activated,
-                // we might have set the preserveLayers flag for the current
-                // browser. Let's clear it.
-                browser.preserveLayers(false);
-              }
-            },
-
-            // This function runs before every event. It fixes up the state
-            // to account for closed tabs.
-            preActions() {
-              this.assert(this.tabbrowser._switcher);
-              this.assert(this.tabbrowser._switcher === this);
-
-              for (let [tab, ] of this.tabState) {
-                if (!tab.linkedBrowser) {
-                  this.tabState.delete(tab);
-                  this.unwarmTab(tab);
-                }
-              }
-
-              if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
-                this.lastVisibleTab = null;
-              }
-              if (this.lastPrimaryTab && !this.lastPrimaryTab.linkedBrowser) {
-                this.lastPrimaryTab = null;
-              }
-              if (this.blankTab && !this.blankTab.linkedBrowser) {
-                this.blankTab = null;
-              }
-              if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
-                this.spinnerHidden();
-                this.spinnerTab = null;
-              }
-              if (this.loadingTab && !this.loadingTab.linkedBrowser) {
-                this.loadingTab = null;
-                this.clearTimer(this.loadTimer);
-                this.loadTimer = null;
-              }
-            },
-
-            // This code runs after we've responded to an event or requested a new
-            // tab. It's expected that we've already updated all the principal
-            // state variables. This function takes care of updating any auxilliary
-            // state.
-            postActions() {
-              // Once we finish loading loadingTab, we null it out. So the state should
-              // always be LOADING.
-              this.assert(!this.loadingTab ||
-                          this.getTabState(this.loadingTab) == this.STATE_LOADING);
-
-              // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
-              // the timer is set only when we're loading something.
-              this.assert(!this.loadTimer || this.loadingTab);
-              this.assert(!this.loadingTab || this.loadTimer);
-
-              // If we're switching to a non-remote tab, there's no need to wait
-              // for it to send layers to the compositor, as this will happen
-              // synchronously. Clearing this here means that in the next step,
-              // we can load the non-remote browser immediately.
-              if (!this.requestedTab.linkedBrowser.isRemoteBrowser) {
-                this.loadingTab = null;
-                if (this.loadTimer) {
-                  this.clearTimer(this.loadTimer);
-                  this.loadTimer = null;
-                }
-              }
-
-              // If we're not loading anything, try loading the requested tab.
-              let stateOfRequestedTab = this.getTabState(this.requestedTab);
-              if (!this.loadTimer && !this.minimizedOrFullyOccluded &&
-                  (stateOfRequestedTab == this.STATE_UNLOADED ||
-                   stateOfRequestedTab == this.STATE_UNLOADING ||
-                   this.warmingTabs.has(this.requestedTab))) {
-                this.assert(stateOfRequestedTab != this.STATE_LOADED);
-                this.loadRequestedTab();
-              }
-
-              // See how many tabs still have work to do.
-              let numPending = 0;
-              let numWarming = 0;
-              for (let [tab, state] of this.tabState) {
-                // Skip print preview browsers since they shouldn't affect tab switching.
-                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
-                  continue;
-                }
-
-                if (state == this.STATE_LOADED && tab !== this.requestedTab) {
-                  numPending++;
-
-                  if (tab !== this.visibleTab) {
-                    numWarming++;
-                  }
-                }
-                if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
-                  numPending++;
-                }
-              }
-
-              this.updateDisplay();
-
-              // It's possible for updateDisplay to trigger one of our own event
-              // handlers, which might cause finish() to already have been called.
-              // Check for that before calling finish() again.
-              if (!this.tabbrowser._switcher) {
-                return;
-              }
-
-              this.maybeFinishTabSwitch();
-
-              if (numWarming > this.tabbrowser.tabWarmingMax) {
-                this.logState("Hit tabWarmingMax");
-                if (this.unloadTimer) {
-                  this.clearTimer(this.unloadTimer);
-                }
-                this.unloadNonRequiredTabs();
-              }
-
-              if (numPending == 0) {
-                this.finish();
-              }
-
-              this.logState("done");
-            },
-
-            // Fires when we're ready to unload unused tabs.
-            onUnloadTimeout() {
-              this.logState("onUnloadTimeout");
-              this.preActions();
-              this.unloadTimer = null;
-
-              this.unloadNonRequiredTabs();
-
-              this.postActions();
-            },
-
-            // If there are any non-visible and non-requested tabs in
-            // STATE_LOADED, sets them to STATE_UNLOADING. Also queues
-            // up the unloadTimer to run onUnloadTimeout if there are still
-            // tabs in the process of unloading.
-            unloadNonRequiredTabs() {
-              this.warmingTabs = new WeakSet();
-              let numPending = 0;
-
-              // Unload any tabs that can be unloaded.
-              for (let [tab, state] of this.tabState) {
-                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
-                  continue;
-                }
-
-                if (state == this.STATE_LOADED &&
-                    !this.maybeVisibleTabs.has(tab) &&
-                    tab !== this.lastVisibleTab &&
-                    tab !== this.loadingTab &&
-                    tab !== this.requestedTab) {
-                  this.setTabState(tab, this.STATE_UNLOADING);
-                }
-
-                if (state != this.STATE_UNLOADED && tab !== this.requestedTab) {
-                  numPending++;
-                }
-              }
-
-              if (numPending) {
-                // Keep the timer going since there may be more tabs to unload.
-                this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
-              }
-            },
-
-            // Fires when an ongoing load has taken too long.
-            onLoadTimeout() {
-              this.logState("onLoadTimeout");
-              this.preActions();
-              this.loadTimer = null;
-              this.loadingTab = null;
-              this.postActions();
-            },
-
-            // Fires when the layers become available for a tab.
-            onLayersReady(browser) {
-              let tab = this.tabbrowser.getTabForBrowser(browser);
-              if (!tab) {
-                // We probably got a layer update from a tab that got before
-                // the switcher was created, or for browser that's not being
-                // tracked by the async tab switcher (like the preloaded about:newtab).
-                return;
-              }
-
-              this.logState(`onLayersReady(${tab._tPos}, ${browser.isRemoteBrowser})`);
-
-              this.assert(this.getTabState(tab) == this.STATE_LOADING ||
-                          this.getTabState(tab) == this.STATE_LOADED);
-              this.setTabState(tab, this.STATE_LOADED);
-              this.unwarmTab(tab);
-
-              if (this.loadingTab === tab) {
-                this.clearTimer(this.loadTimer);
-                this.loadTimer = null;
-                this.loadingTab = null;
-              }
-            },
-
-            // Fires when we paint the screen. Any tab switches we initiated
-            // previously are done, so there's no need to keep the old layers
-            // around.
-            onPaint() {
-              this.maybeVisibleTabs.clear();
-            },
-
-            // Called when we're done clearing the layers for a tab.
-            onLayersCleared(browser) {
-              let tab = this.tabbrowser.getTabForBrowser(browser);
-              if (tab) {
-                this.logState(`onLayersCleared(${tab._tPos})`);
-                this.assert(this.getTabState(tab) == this.STATE_UNLOADING ||
-                            this.getTabState(tab) == this.STATE_UNLOADED);
-                this.setTabState(tab, this.STATE_UNLOADED);
-              }
-            },
-
-            // Called when a tab switches from remote to non-remote. In this case
-            // a MozLayerTreeReady notification that we requested may never fire,
-            // so we need to simulate it.
-            onRemotenessChange(tab) {
-              this.logState(`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`);
-              if (!tab.linkedBrowser.isRemoteBrowser) {
-                if (this.getTabState(tab) == this.STATE_LOADING) {
-                  this.onLayersReady(tab.linkedBrowser);
-                } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
-                  this.onLayersCleared(tab.linkedBrowser);
-                }
-              } else if (this.getTabState(tab) == this.STATE_LOADED) {
-                // A tab just changed from non-remote to remote, which means
-                // that it's gone back into the STATE_LOADING state until
-                // it sends up a layer tree.
-                this.setTabState(tab, this.STATE_LOADING);
-              }
-            },
-
-            // Called when a tab has been removed, and the browser node is
-            // about to be removed from the DOM.
-            onTabRemoved(tab) {
-              if (this.lastVisibleTab == tab) {
-                // The browser that was being presented to the user is
-                // going to be removed during this tick of the event loop.
-                // This will cause us to show a tab spinner instead.
-                this.preActions();
-                this.lastVisibleTab = null;
-                this.postActions();
-              }
-            },
-
-            onSizeModeOrOcclusionStateChange() {
-              if (this.minimizedOrFullyOccluded) {
-                for (let [tab, state] of this.tabState) {
-                  // Skip print preview browsers since they shouldn't affect tab switching.
-                  if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
-                    continue;
-                  }
-
-                  if (state == this.STATE_LOADING || state == this.STATE_LOADED) {
-                    this.setTabState(tab, this.STATE_UNLOADING);
-                  }
-                }
-                if (this.loadTimer) {
-                  this.clearTimer(this.loadTimer);
-                  this.loadTimer = null;
-                }
-                this.loadingTab = null;
-              } else {
-                // We're no longer minimized or occluded. This means we might want
-                // to activate the current tab's docShell.
-                this.maybeActivateDocShell(gBrowser.selectedTab);
-              }
-            },
-
-            onSwapDocShells(ourBrowser, otherBrowser) {
-              // This event fires before the swap. ourBrowser is from
-              // our window. We save the state of otherBrowser since ourBrowser
-              // needs to take on that state at the end of the swap.
-
-              let otherTabbrowser = otherBrowser.ownerGlobal.gBrowser;
-              let otherState;
-              if (otherTabbrowser && otherTabbrowser._switcher) {
-                let otherTab = otherTabbrowser.getTabForBrowser(otherBrowser);
-                let otherSwitcher = otherTabbrowser._switcher;
-                otherState = otherSwitcher.getTabState(otherTab);
-              } else {
-                otherState = (otherBrowser.docShellIsActive
-                              ? this.STATE_LOADED
-                              : this.STATE_UNLOADED);
-              }
-
-              if (!this.swapMap) {
-                this.swapMap = new WeakMap();
-              }
-              this.swapMap.set(otherBrowser, {
-                state: otherState,
-              });
-            },
-
-            onEndSwapDocShells(ourBrowser, otherBrowser) {
-              // The swap has happened. We reset the loadingTab in
-              // case it has been swapped. We also set ourBrowser's state
-              // to whatever otherBrowser's state was before the swap.
-
-              if (this.loadTimer) {
-                // Clearing the load timer means that we will
-                // immediately display a spinner if ourBrowser isn't
-                // ready yet. Typically it will already be ready
-                // though. If it's not, we're probably in a new window,
-                // in which case we have no other tabs to display anyway.
-                this.clearTimer(this.loadTimer);
-                this.loadTimer = null;
-              }
-              this.loadingTab = null;
-
-              let { state: otherState } = this.swapMap.get(otherBrowser);
-
-              this.swapMap.delete(otherBrowser);
-
-              let ourTab = this.tabbrowser.getTabForBrowser(ourBrowser);
-              if (ourTab) {
-                this.setTabStateNoAction(ourTab, otherState);
-              }
-            },
-
-            shouldActivateDocShell(browser) {
-              let tab = this.tabbrowser.getTabForBrowser(browser);
-              let state = this.getTabState(tab);
-              return state == this.STATE_LOADING || state == this.STATE_LOADED;
-            },
-
-            activateBrowserForPrintPreview(browser) {
-              let tab = this.tabbrowser.getTabForBrowser(browser);
-              let state = this.getTabState(tab);
-              if (state != this.STATE_LOADING &&
-                  state != this.STATE_LOADED) {
-                this.setTabState(tab, this.STATE_LOADING);
-                this.logState("Activated browser " + this.tinfo(tab) + " for print preview");
-              }
-            },
-
-            canWarmTab(tab) {
-              if (!this.tabbrowser.tabWarmingEnabled) {
-                return false;
-              }
-
-              if (!tab) {
-                return false;
-              }
-
-              // If the tab is not yet inserted, closing, not remote,
-              // crashed, already visible, or already requested, warming
-              // up the tab makes no sense.
-              if (this.minimizedOrFullyOccluded ||
-                  !tab.linkedPanel ||
-                  tab.closing ||
-                  !tab.linkedBrowser.isRemoteBrowser ||
-                  !tab.linkedBrowser.frameLoader.tabParent) {
-                return false;
-              }
-
-              // Similarly, if the tab is already in STATE_LOADING or
-              // STATE_LOADED somehow, there's no point in trying to
-              // warm it up.
-              let state = this.getTabState(tab);
-              if (state === this.STATE_LOADING ||
-                  state === this.STATE_LOADED) {
-                return false;
-              }
-
-              return true;
-            },
-
-            unwarmTab(tab) {
-              this.warmingTabs.delete(tab);
-            },
-
-            warmupTab(tab) {
-              if (!this.canWarmTab(tab)) {
-                return;
-              }
-
-              this.logState("warmupTab " + this.tinfo(tab));
-
-              this.warmingTabs.add(tab);
-              this.setTabState(tab, this.STATE_LOADING);
-              this.suppressDisplayPortAndQueueUnload(tab,
-                this.tabbrowser.tabWarmingUnloadDelay);
-            },
-
-            // Called when the user asks to switch to a given tab.
-            requestTab(tab) {
-              if (tab === this.requestedTab) {
-                return;
-              }
-
-              if (this.tabbrowser.tabWarmingEnabled) {
-                let warmingState = "disqualified";
-
-                if (this.warmingTabs.has(tab)) {
-                  let tabState = this.getTabState(tab);
-                  if (tabState == this.STATE_LOADING) {
-                    warmingState = "stillLoading";
-                  } else if (tabState == this.STATE_LOADED) {
-                    warmingState = "loaded";
-                  }
-                } else if (this.canWarmTab(tab)) {
-                  warmingState = "notWarmed";
-                }
-
-                Services.telemetry
-                        .getHistogramById("FX_TAB_SWITCH_REQUEST_TAB_WARMING_STATE")
-                        .add(warmingState);
-              }
-
-              this._requestingTab = true;
-              this.logState("requestTab " + this.tinfo(tab));
-              this.startTabSwitch();
-
-              this.requestedTab = tab;
-
-              tab.linkedBrowser.setAttribute("primary", "true");
-              if (this.lastPrimaryTab && this.lastPrimaryTab != tab) {
-                this.lastPrimaryTab.linkedBrowser.removeAttribute("primary");
-              }
-              this.lastPrimaryTab = tab;
-
-              this.suppressDisplayPortAndQueueUnload(this.requestedTab, this.UNLOAD_DELAY);
-              this._requestingTab = false;
-            },
-
-            suppressDisplayPortAndQueueUnload(tab, unloadTimeout) {
-              let browser = tab.linkedBrowser;
-              let fl = browser.frameLoader;
-
-              if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
-                fl.tabParent.suppressDisplayport(true);
-                this.activeSuppressDisplayport.add(fl.tabParent);
-              }
-
-              this.preActions();
-
-              if (this.unloadTimer) {
-                this.clearTimer(this.unloadTimer);
-              }
-              this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), unloadTimeout);
-
-              this.postActions();
-            },
-
-            handleEvent(event, delayed = false) {
-              if (this._processing) {
-                this.setTimer(() => this.handleEvent(event, true), 0);
-                return;
-              }
-              if (delayed && this.tabbrowser._switcher != this) {
-                // if we delayed processing this event, we might be out of date, in which
-                // case we drop the delayed events
-                return;
-              }
-              this._processing = true;
-              this.preActions();
-
-              if (event.type == "MozLayerTreeReady") {
-                this.onLayersReady(event.originalTarget);
-              } if (event.type == "MozAfterPaint") {
-                this.onPaint();
-              } else if (event.type == "MozLayerTreeCleared") {
-                this.onLayersCleared(event.originalTarget);
-              } else if (event.type == "TabRemotenessChange") {
-                this.onRemotenessChange(event.target);
-              } else if (event.type == "sizemodechange" ||
-                         event.type == "occlusionstatechange") {
-                this.onSizeModeOrOcclusionStateChange();
-              } else if (event.type == "SwapDocShells") {
-                this.onSwapDocShells(event.originalTarget, event.detail);
-              } else if (event.type == "EndSwapDocShells") {
-                this.onEndSwapDocShells(event.originalTarget, event.detail);
-              }
-
-              this.postActions();
-              this._processing = false;
-            },
-
-            /*
-             * Telemetry and Profiler related helpers for recording tab switch
-             * timing.
-             */
-
-            startTabSwitch() {
-              TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
-              TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
-              this.addMarker("AsyncTabSwitch:Start");
-              this.switchInProgress = true;
-            },
-
-            /**
-             * Something has occurred that might mean that we've completed
-             * the tab switch (layers are ready, paints are done, spinners
-             * are hidden). This checks to make sure all conditions are
-             * satisfied, and then records the tab switch as finished.
-             */
-            maybeFinishTabSwitch() {
-              if (this.switchInProgress && this.requestedTab &&
-                  (this.getTabState(this.requestedTab) == this.STATE_LOADED ||
-                   this.requestedTab === this.blankTab)) {
-                // After this point the tab has switched from the content thread's point of view.
-                // The changes will be visible after the next refresh driver tick + composite.
-                let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
-                if (time != -1) {
-                  TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
-                  this.log("DEBUG: tab switch time = " + time);
-                  this.addMarker("AsyncTabSwitch:Finish");
-                }
-                this.switchInProgress = false;
-              }
-            },
-
-            spinnerDisplayed() {
-              this.assert(!this.spinnerTab);
-              let browser = this.requestedTab.linkedBrowser;
-              this.assert(browser.isRemoteBrowser);
-              TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
-              // We have a second, similar probe for capturing recordings of
-              // when the spinner is displayed for very long periods.
-              TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window);
-              this.addMarker("AsyncTabSwitch:SpinnerShown");
-            },
-
-            spinnerHidden() {
-              this.assert(this.spinnerTab);
-              this.log("DEBUG: spinner time = " +
-                       TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window));
-              TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
-              TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window);
-              this.addMarker("AsyncTabSwitch:SpinnerHidden");
-              // we do not get a onPaint after displaying the spinner
-            },
-
-            addMarker(marker) {
-              if (Services.profiler) {
-                Services.profiler.AddMarker(marker);
-              }
-            },
-
-            /*
-             * Debug related logging for switcher.
-             */
-
-            _useDumpForLogging: false,
-            _logInit: false,
-
-            logging() {
-              if (this._useDumpForLogging)
-                return true;
-              if (this._logInit)
-                return this._shouldLog;
-              let result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming", false);
-              this._shouldLog = result;
-              this._logInit = true;
-              return this._shouldLog;
-            },
-
-            tinfo(tab) {
-              if (tab) {
-                return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
-              }
-              return "null";
-            },
-
-            log(s) {
-              if (!this.logging())
-                return;
-              if (this._useDumpForLogging) {
-                dump(s + "\n");
-              } else {
-                Services.console.logStringMessage(s);
-              }
-            },
-
-            logState(prefix) {
-              if (!this.logging())
-                return;
-
-              let accum = prefix + " ";
-              for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
-                let tab = this.tabbrowser.tabs[i];
-                let state = this.getTabState(tab);
-                let isWarming = this.warmingTabs.has(tab);
-
-                accum += i + ":";
-                if (tab === this.lastVisibleTab) accum += "V";
-                if (tab === this.loadingTab) accum += "L";
-                if (tab === this.requestedTab) accum += "R";
-                if (tab === this.blankTab) accum += "B";
-                if (isWarming) accum += "(W)";
-                if (state == this.STATE_LOADED) accum += "(+)";
-                if (state == this.STATE_LOADING) accum += "(+?)";
-                if (state == this.STATE_UNLOADED) accum += "(-)";
-                if (state == this.STATE_UNLOADING) accum += "(-?)";
-                accum += " ";
-              }
-              if (this._useDumpForLogging) {
-                dump(accum + "\n");
-              } else {
-                Services.console.logStringMessage(accum);
-              }
-            },
-          };
-          this._switcher = switcher;
-          switcher.init();
-          return switcher;
-        ]]></body>
-      </method>
-
-      <method name="warmupTab">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            if (gMultiProcessBrowser) {
-              this._getSwitcher().warmupTab(aTab);
-            }
-          ]]>
-        </body>
-      </method>
-
-      <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
-           MAKE SURE TO ADD IT HERE AS WELL. -->
-      <property name="canGoBack"
-                onget="return this.mCurrentBrowser.canGoBack;"
-                readonly="true"/>
-
-      <property name="canGoForward"
-                onget="return this.mCurrentBrowser.canGoForward;"
-                readonly="true"/>
-
-      <method name="goBack">
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.goBack();
-          ]]>
-        </body>
-      </method>
-
-      <method name="goForward">
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.goForward();
-          ]]>
-        </body>
-      </method>
-
-      <method name="reload">
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.reload();
-          ]]>
-        </body>
-      </method>
-
-      <method name="reloadWithFlags">
-        <parameter name="aFlags"/>
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.reloadWithFlags(aFlags);
-          ]]>
-        </body>
-      </method>
-
-      <method name="stop">
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.stop();
-          ]]>
-        </body>
-      </method>
-
-      <!-- throws exception for unknown schemes -->
-      <method name="loadURI">
-        <parameter name="aURI"/>
-        <parameter name="aReferrerURI"/>
-        <parameter name="aCharset"/>
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
-          ]]>
-        </body>
-      </method>
-
-      <!-- throws exception for unknown schemes -->
-      <method name="loadURIWithFlags">
-        <parameter name="aURI"/>
-        <parameter name="aFlags"/>
-        <parameter name="aReferrerURI"/>
-        <parameter name="aCharset"/>
-        <parameter name="aPostData"/>
-        <body>
-          <![CDATA[
-            // Note - the callee understands both:
-            // (a) loadURIWithFlags(aURI, aFlags, ...)
-            // (b) loadURIWithFlags(aURI, { flags: aFlags, ... })
-            // Forwarding it as (a) here actually supports both (a) and (b),
-            // so you can call us either way too.
-            return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
-          ]]>
-        </body>
-      </method>
-
-      <method name="goHome">
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.goHome();
-          ]]>
-        </body>
-      </method>
-
-      <property name="homePage">
-        <getter>
-          <![CDATA[
-            return this.mCurrentBrowser.homePage;
-          ]]>
-        </getter>
-        <setter>
-          <![CDATA[
-            this.mCurrentBrowser.homePage = val;
-            return val;
-          ]]>
-        </setter>
-      </property>
-
-      <method name="gotoIndex">
-        <parameter name="aIndex"/>
-        <body>
-          <![CDATA[
-            return this.mCurrentBrowser.gotoIndex(aIndex);
-          ]]>
-        </body>
-      </method>
-
-      <property name="currentURI"
-                onget="return this.mCurrentBrowser.currentURI;"
-                readonly="true"/>
-
-      <property name="finder"
-                onget="return this.mCurrentBrowser.finder"
-                readonly="true"/>
-
-      <property name="docShell"
-                onget="return this.mCurrentBrowser.docShell"
-                readonly="true"/>
-
-      <property name="webNavigation"
-                onget="return this.mCurrentBrowser.webNavigation"
-                readonly="true"/>
-
-      <property name="webBrowserFind"
-                readonly="true"
-                onget="return this.mCurrentBrowser.webBrowserFind"/>
-
-      <property name="webProgress"
-                readonly="true"
-                onget="return this.mCurrentBrowser.webProgress"/>
-
-      <property name="contentWindow"
-                readonly="true"
-                onget="return this.mCurrentBrowser.contentWindow"/>
-
-      <property name="contentWindowAsCPOW"
-                readonly="true"
-                onget="return this.mCurrentBrowser.contentWindowAsCPOW"/>
-
-      <property name="sessionHistory"
-                onget="return this.mCurrentBrowser.sessionHistory;"
-                readonly="true"/>
-
-      <property name="markupDocumentViewer"
-                onget="return this.mCurrentBrowser.markupDocumentViewer;"
-                readonly="true"/>
-
-      <property name="contentDocument"
-                onget="return this.mCurrentBrowser.contentDocument;"
-                readonly="true"/>
-
-      <property name="contentDocumentAsCPOW"
-                onget="return this.mCurrentBrowser.contentDocumentAsCPOW;"
-                readonly="true"/>
-
-      <property name="contentTitle"
-                onget="return this.mCurrentBrowser.contentTitle;"
-                readonly="true"/>
-
-      <property name="contentPrincipal"
-                onget="return this.mCurrentBrowser.contentPrincipal;"
-                readonly="true"/>
-
-      <property name="securityUI"
-                onget="return this.mCurrentBrowser.securityUI;"
-                readonly="true"/>
-
-      <property name="fullZoom"
-                onget="return this.mCurrentBrowser.fullZoom;"
-                onset="this.mCurrentBrowser.fullZoom = val;"/>
-
-      <property name="textZoom"
-                onget="return this.mCurrentBrowser.textZoom;"
-                onset="this.mCurrentBrowser.textZoom = val;"/>
-
-      <property name="isSyntheticDocument"
-                onget="return this.mCurrentBrowser.isSyntheticDocument;"
-                readonly="true"/>
-
-      <method name="_handleKeyDownEvent">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          if (!aEvent.isTrusted) {
-            // Don't let untrusted events mess with tabs.
-            return;
-          }
-
-          if (aEvent.altKey)
-            return;
-
-          // Don't check if the event was already consumed because tab
-          // navigation should always work for better user experience.
-
-          if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) {
-            switch (aEvent.keyCode) {
-              case aEvent.DOM_VK_PAGE_UP:
-                this.moveTabBackward();
-                aEvent.preventDefault();
-                return;
-              case aEvent.DOM_VK_PAGE_DOWN:
-                this.moveTabForward();
-                aEvent.preventDefault();
-                return;
-            }
-          }
-
-          if (AppConstants.platform != "macosx") {
-            if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
-                aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
-                !this.mCurrentTab.pinned) {
-              this.removeCurrentTab({animate: true});
-              aEvent.preventDefault();
-            }
-          }
-        ]]></body>
-      </method>
-
-      <method name="_handleKeyPressEventMac">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          if (!aEvent.isTrusted) {
-            // Don't let untrusted events mess with tabs.
-            return;
-          }
-
-          if (aEvent.altKey)
-            return;
-
-          if (AppConstants.platform == "macosx") {
-            if (!aEvent.metaKey)
-              return;
-
-            var offset = 1;
-            switch (aEvent.charCode) {
-              case "}".charCodeAt(0):
-                offset = -1;
-              case "{".charCodeAt(0):
-                if (window.getComputedStyle(this).direction == "ltr")
-                  offset *= -1;
-                this.tabContainer.advanceSelectedTab(offset, true);
-                aEvent.preventDefault();
-            }
-          }
-        ]]></body>
-      </method>
-
-      <property name="userTypedValue"
-                onget="return this.mCurrentBrowser.userTypedValue;"
-                onset="return this.mCurrentBrowser.userTypedValue = val;"/>
-
-      <method name="createTooltip">
-        <parameter name="event"/>
-        <body><![CDATA[
-          event.stopPropagation();
-          var tab = document.tooltipNode;
-          if (tab.localName != "tab") {
-            event.preventDefault();
-            return;
-          }
-
-          let stringWithShortcut = (stringId, keyElemId) => {
-            let keyElem = document.getElementById(keyElemId);
-            let shortcut = ShortcutUtils.prettifyShortcut(keyElem);
-            return gTabBrowserBundle.formatStringFromName(stringId, [shortcut], 1);
-          };
-
-          var label;
-          if (tab.mOverCloseButton) {
-            label = tab.selected ?
-                    stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") :
-                    gTabBrowserBundle.GetStringFromName("tabs.closeTab.tooltip");
-          } else if (tab._overPlayingIcon) {
-            let stringID;
-            if (tab.selected) {
-              stringID = tab.linkedBrowser.audioMuted ?
-                "tabs.unmuteAudio.tooltip" :
-                "tabs.muteAudio.tooltip";
-              label = stringWithShortcut(stringID, "key_toggleMute");
-            } else {
-              if (tab.hasAttribute("activemedia-blocked")) {
-                stringID = "tabs.unblockAudio.tooltip";
-              } else {
-                stringID = tab.linkedBrowser.audioMuted ?
-                  "tabs.unmuteAudio.background.tooltip" :
-                  "tabs.muteAudio.background.tooltip";
-              }
-
-              label = gTabBrowserBundle.GetStringFromName(stringID);
-            }
-          } else {
-            label = tab._fullLabel || tab.getAttribute("label");
-            if (AppConstants.NIGHTLY_BUILD &&
-                tab.linkedBrowser &&
-                tab.linkedBrowser.isRemoteBrowser &&
-                tab.linkedBrowser.frameLoader) {
-              label += " (pid " + tab.linkedBrowser.frameLoader.tabParent.osPid + ")";
-            }
-            if (tab.userContextId) {
-              label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)], 2);
-            }
-          }
-
-          event.target.setAttribute("label", label);
-        ]]></body>
-      </method>
-
-      <method name="handleEvent">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          switch (aEvent.type) {
-            case "keydown":
-              this._handleKeyDownEvent(aEvent);
-              break;
-            case "keypress":
-              this._handleKeyPressEventMac(aEvent);
-              break;
-            case "sizemodechange":
-            case "occlusionstatechange":
-              if (aEvent.target == window && !this._switcher) {
-                this.mCurrentBrowser.preserveLayers(
-                  window.windowState == window.STATE_MINIMIZED || window.isFullyOccluded);
-                this.mCurrentBrowser.docShellIsActive = this.shouldActivateDocShell(this.mCurrentBrowser);
-              }
-              break;
-          }
-        ]]></body>
-      </method>
-
-      <method name="receiveMessage">
-        <parameter name="aMessage"/>
-        <body><![CDATA[
-          let data = aMessage.data;
-          let browser = aMessage.target;
-
-          switch (aMessage.name) {
-            case "DOMTitleChanged": {
-              let tab = this.getTabForBrowser(browser);
-              if (!tab || tab.hasAttribute("pending"))
-                return undefined;
-              let titleChanged = this.setTabTitle(tab);
-              if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
-                tab.setAttribute("titlechanged", "true");
-              break;
-            }
-            case "DOMWindowClose": {
-              if (this.tabs.length == 1) {
-                // We already did PermitUnload in the content process
-                // for this tab (the only one in the window). So we don't
-                // need to do it again for any tabs.
-                window.skipNextCanClose = true;
-                window.close();
-                return undefined;
-              }
-
-              let tab = this.getTabForBrowser(browser);
-              if (tab) {
-                // Skip running PermitUnload since it already happened in
-                // the content process.
-                this.removeTab(tab, {skipPermitUnload: true});
-              }
-              break;
-            }
-            case "contextmenu": {
-              openContextMenu(aMessage);
-              break;
-            }
-            case "DOMWindowFocus": {
-              let tab = this.getTabForBrowser(browser);
-              if (!tab)
-                return undefined;
-              this.selectedTab = tab;
-              window.focus();
-              break;
-            }
-            case "Browser:Init": {
-              let tab = this.getTabForBrowser(browser);
-              if (!tab)
-                return undefined;
-
-              this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
-              browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });
-              break;
-            }
-            case "Browser:WindowCreated": {
-              let tab = this.getTabForBrowser(browser);
-              if (tab && data.userContextId) {
-                ContextualIdentityService.telemetry(data.userContextId);
-                tab.setUserContextId(data.userContextId);
-              }
-
-              // We don't want to update the container icon and identifier if
-              // this is not the selected browser.
-              if (browser == gBrowser.selectedBrowser) {
-                updateUserContextUIIndicator();
-              }
-
-              break;
-            }
-            case "Findbar:Keypress": {
-              let tab = this.getTabForBrowser(browser);
-              // If the find bar for this tab is not yet alive, only initialize
-              // it if there's a possibility FindAsYouType will be used.
-              // There's no point in doing it for most random keypresses.
-              if (!this.isFindBarInitialized(tab) &&
-                data.shouldFastFind) {
-                let shouldFastFind = this._findAsYouType;
-                if (!shouldFastFind) {
-                  // Please keep in sync with toolkit/content/widgets/findbar.xml
-                  const FAYT_LINKS_KEY = "'";
-                  const FAYT_TEXT_KEY = "/";
-                  let charCode = data.fakeEvent.charCode;
-                  let key = charCode ? String.fromCharCode(charCode) : null;
-                  shouldFastFind = key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY;
-                }
-                if (shouldFastFind) {
-                  // Make sure we return the result.
-                  return this.getFindBar(tab).receiveMessage(aMessage);
-                }
-              }
-              break;
-            }
-            case "RefreshBlocker:Blocked": {
-              // The data object is expected to contain the following properties:
-              //  - URI (string)
-              //     The URI that a page is attempting to refresh or redirect to.
-              //  - delay (int)
-              //     The delay (in milliseconds) before the page was going to
-              //     reload or redirect.
-              //  - sameURI (bool)
-              //     true if we're refreshing the page. false if we're redirecting.
-              //  - outerWindowID (int)
-              //     The outerWindowID of the frame that requested the refresh or
-              //     redirect.
-
-              let brandBundle = document.getElementById("bundle_brand");
-              let brandShortName = brandBundle.getString("brandShortName");
-              let message =
-                gNavigatorBundle.getFormattedString("refreshBlocked." +
-                                                    (data.sameURI ? "refreshLabel"
-                                                                  : "redirectLabel"),
-                                                    [brandShortName]);
-
-              let notificationBox = this.getNotificationBox(browser);
-              let notification = notificationBox.getNotificationWithValue("refresh-blocked");
-
-              if (notification) {
-                notification.label = message;
-              } else {
-                let refreshButtonText =
-                  gNavigatorBundle.getString("refreshBlocked.goButton");
-                let refreshButtonAccesskey =
-                  gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
-
-                let buttons = [{
-                  label: refreshButtonText,
-                  accessKey: refreshButtonAccesskey,
-                  callback() {
-                    if (browser.messageManager) {
-                      browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
-                    }
-                  }
-                }];
-
-                notificationBox.appendNotification(message, "refresh-blocked",
-                                                   "chrome://browser/skin/notification-icons/popup.svg",
-                                                   notificationBox.PRIORITY_INFO_MEDIUM,
-                                                   buttons);
-              }
-              break;
-            }
-          }
-          return undefined;
-        ]]></body>
-      </method>
-
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body><![CDATA[
-          switch (aTopic) {
-            case "contextual-identity-updated": {
-              for (let tab of this.tabs) {
-                if (tab.getAttribute("usercontextid") == aData) {
-                  ContextualIdentityService.setTabStyle(tab);
-                }
-              }
-              break;
-            }
-            case "nsPref:changed": {
-              // This is the only pref observed.
-              this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
-              break;
-            }
-          }
-        ]]></body>
-      </method>
-
-      <method name="_updateNewTabVisibility">
-        <body><![CDATA[
-          // Helper functions to help deal with customize mode wrapping some items
-          let wrap = n => n.parentNode.localName == "toolbarpaletteitem" ? n.parentNode : n;
-          let unwrap = n => n && n.localName == "toolbarpaletteitem" ? n.firstElementChild : n;
-
-          let sib = this.tabContainer;
-          do {
-            sib = unwrap(wrap(sib).nextElementSibling);
-          } while (sib && sib.hidden);
-
-          const kAttr = "hasadjacentnewtabbutton";
-          if (sib && sib.id == "new-tab-button") {
-            this.tabContainer.setAttribute(kAttr, "true");
-          } else {
-            this.tabContainer.removeAttribute(kAttr);
-          }
-        ]]></body>
-      </method>
-
-      <method name="onWidgetAfterDOMChange">
-        <parameter name="aNode"/>
-        <parameter name="aNextNode"/>
-        <parameter name="aContainer"/>
-        <body><![CDATA[
-          if (aContainer.ownerDocument == document &&
-              aContainer.id == "TabsToolbar") {
-            this._updateNewTabVisibility();
-          }
-        ]]></body>
-      </method>
-      <method name="onAreaNodeRegistered">
-        <parameter name="aArea"/>
-        <parameter name="aContainer"/>
-        <body><![CDATA[
-          if (aContainer.ownerDocument == document &&
-              aArea == "TabsToolbar") {
-            this._updateNewTabVisibility();
-          }
-        ]]></body>
-      </method>
-      <method name="onAreaReset">
-        <parameter name="aArea"/>
-        <parameter name="aContainer"/>
-        <body><![CDATA[
-          this.onAreaNodeRegistered(aArea, aContainer);
-        ]]></body>
-      </method>
-
-      <field name="_tabMinWidthLimit">50</field>
-      <property name="tabMinWidth">
-        <setter><![CDATA[
-          this.tabContainer.style.setProperty("--tab-min-width", val + "px");
-          return val;
-        ]]></setter>
-      </property>
-
-      <constructor>
-        <![CDATA[
-          this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
-          this.mCurrentBrowser.permanentKey = {};
-
-          CustomizableUI.addListener(this);
-          this._updateNewTabVisibility();
-
-          Services.obs.addObserver(this, "contextual-identity-updated");
-
-          this.mCurrentTab = this.tabContainer.firstChild;
-          const nsIEventListenerService =
-            Components.interfaces.nsIEventListenerService;
-          let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
-                              .getService(nsIEventListenerService);
-          els.addSystemEventListener(document, "keydown", this, false);
-          if (AppConstants.platform == "macosx") {
-            els.addSystemEventListener(document, "keypress", this, false);
-          }
-          window.addEventListener("sizemodechange", this);
-          window.addEventListener("occlusionstatechange", this);
-
-          var uniqueId = this._generateUniquePanelID();
-          this.mPanelContainer.childNodes[0].id = uniqueId;
-          this.mCurrentTab.linkedPanel = uniqueId;
-          this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
-          this.mCurrentTab._tPos = 0;
-          this.mCurrentTab._fullyOpen = true;
-          this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
-          this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
-
-          // set up the shared autoscroll popup
-          this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
-          this._autoScrollPopup.id = "autoscroller";
-          this.appendChild(this._autoScrollPopup);
-          this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
-          this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
-
-          // Hook up the event listeners to the first browser
-          var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
-          const nsIWebProgress = Components.interfaces.nsIWebProgress;
-          const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
-                                   .createInstance(nsIWebProgress);
-          filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL);
-          this._tabListeners.set(this.mCurrentTab, tabListener);
-          this._tabFilters.set(this.mCurrentTab, filter);
-          this.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL);
-
-          if (Services.prefs.getBoolPref("browser.display.use_system_colors"))
-            this.style.backgroundColor = "-moz-default-background-color";
-          else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2)
-            this.style.backgroundColor = Services.prefs.getCharPref("browser.display.background_color");
-
-          let messageManager = window.getGroupMessageManager("browsers");
-
-          let remote = window.QueryInterface(Ci.nsIInterfaceRequestor)
-            .getInterface(Ci.nsIWebNavigation)
-            .QueryInterface(Ci.nsILoadContext)
-            .useRemoteTabs;
-          if (remote) {
-            messageManager.addMessageListener("DOMTitleChanged", this);
-            messageManager.addMessageListener("DOMWindowClose", this);
-            window.messageManager.addMessageListener("contextmenu", this);
-            messageManager.addMessageListener("Browser:Init", this);
-
-            // If this window has remote tabs, switch to our tabpanels fork
-            // which does asynchronous tab switching.
-            this.mPanelContainer.classList.add("tabbrowser-tabpanels");
-          } else {
-            this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
-                                              this.mCurrentBrowser);
-          }
-          messageManager.addMessageListener("DOMWindowFocus", this);
-          messageManager.addMessageListener("RefreshBlocker:Blocked", this);
-          messageManager.addMessageListener("Browser:WindowCreated", this);
-
-          // To correctly handle keypresses for potential FindAsYouType, while
-          // the tab's find bar is not yet initialized.
-          this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
-          Services.prefs.addObserver("accessibility.typeaheadfind", this);
-          messageManager.addMessageListener("Findbar:Keypress", this);
-
-          XPCOMUtils.defineLazyPreferenceGetter(this, "animationsEnabled",
-                                                "toolkit.cosmeticAnimations.enabled", true);
-          XPCOMUtils.defineLazyPreferenceGetter(this, "schedulePressureDefaultCount",
-                                                "browser.schedulePressure.defaultCount", 3);
-          XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingEnabled",
-                                                "browser.tabs.remote.warmup.enabled", false);
-          XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingMax",
-                                                "browser.tabs.remote.warmup.maxTabs", 3);
-          XPCOMUtils.defineLazyPreferenceGetter(this, "tabWarmingUnloadDelay" /* ms */,
-                                                "browser.tabs.remote.warmup.unloadDelayMs", 2000);
-          XPCOMUtils.defineLazyPreferenceGetter(this, "tabMinWidthPref",
-                                                "browser.tabs.tabMinWidth", this._tabMinWidthLimit,
-            (pref, prevValue, newValue) => this.tabMinWidth = newValue,
-            newValue => Math.max(newValue, this._tabMinWidthLimit),
-          );
-
-          this.tabMinWidth = this.tabMinWidthPref;
-        ]]>
-      </constructor>
-
-      <method name="_generateUniquePanelID">
-        <body><![CDATA[
-          if (!this._uniquePanelIDCounter) {
-            this._uniquePanelIDCounter = 0;
-          }
-
-          let outerID = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDOMWindowUtils)
-                              .outerWindowID;
-
-          // We want panel IDs to be globally unique, that's why we include the
-          // window ID. We switched to a monotonic counter as Date.now() lead
-          // to random failures because of colliding IDs.
-          return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
-        ]]></body>
-      </method>
-
-      <destructor>
-        <![CDATA[
-          Services.obs.removeObserver(this, "contextual-identity-updated");
-
-          CustomizableUI.removeListener(this);
-
-          for (let tab of this.tabs) {
-            let browser = tab.linkedBrowser;
-            if (browser.registeredOpenURI) {
-              this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
-                                                       browser.getAttribute("usercontextid") || 0);
-              delete browser.registeredOpenURI;
-            }
-
-            let filter = this._tabFilters.get(tab);
-            if (filter) {
-              browser.webProgress.removeProgressListener(filter);
-
-              let listener = this._tabListeners.get(tab);
-              if (listener) {
-                filter.removeProgressListener(listener);
-                listener.destroy();
-              }
-
-              this._tabFilters.delete(tab);
-              this._tabListeners.delete(tab);
-            }
-          }
-          const nsIEventListenerService =
-            Components.interfaces.nsIEventListenerService;
-          let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
-                              .getService(nsIEventListenerService);
-          els.removeSystemEventListener(document, "keydown", this, false);
-          if (AppConstants.platform == "macosx") {
-            els.removeSystemEventListener(document, "keypress", this, false);
-          }
-          window.removeEventListener("sizemodechange", this);
-          window.removeEventListener("occlusionstatechange", this);
-
-          if (gMultiProcessBrowser) {
-            let messageManager = window.getGroupMessageManager("browsers");
-            messageManager.removeMessageListener("DOMTitleChanged", this);
-            window.messageManager.removeMessageListener("contextmenu", this);
-
-            if (this._switcher) {
-              this._switcher.destroy();
-            }
-          }
-
-          Services.prefs.removeObserver("accessibility.typeaheadfind", this);
-        ]]>
-      </destructor>
-
-      <field name="_soundPlayingAttrRemovalTimer">0</field>
-      <field name="_hoverTabTimer">null</field>
-    </implementation>
-
-    <handlers>
-      <handler event="DOMWindowClose" phase="capturing">
-        <![CDATA[
-          if (!event.isTrusted)
-            return;
-
-          if (this.tabs.length == 1) {
-            // We already did PermitUnload in nsGlobalWindow::Close
-            // for this tab. There are no other tabs we need to do
-            // PermitUnload for.
-            window.skipNextCanClose = true;
-            return;
-          }
-
-          var tab = this._getTabForContentWindow(event.target);
-          if (tab) {
-            // Skip running PermitUnload since it already happened.
-            this.removeTab(tab, {skipPermitUnload: true});
-            event.preventDefault();
-          }
-        ]]>
-      </handler>
-      <handler event="DOMWillOpenModalDialog" phase="capturing">
-        <![CDATA[
-          if (!event.isTrusted)
-            return;
-
-          let targetIsWindow = event.target instanceof Window;
-
-          // We're about to open a modal dialog, so figure out for which tab:
-          // If this is a same-process modal dialog, then we're given its DOM
-          // window as the event's target. For remote dialogs, we're given the
-          // browser, but that's in the originalTarget and not the target,
-          // because it's across the tabbrowser's XBL boundary.
-          let tabForEvent = targetIsWindow ?
-                            this._getTabForContentWindow(event.target.top) :
-                            this.getTabForBrowser(event.originalTarget);
-
-          // Focus window for beforeunload dialog so it is seen but don't
-          // steal focus from other applications.
-          if (event.detail &&
-              event.detail.tabPrompt &&
-              event.detail.inPermitUnload &&
-              Services.focus.activeWindow)
-            window.focus();
-
-          // Don't need to act if the tab is already selected or if there isn't
-          // a tab for the event (e.g. for the webextensions options_ui remote
-          // browsers embedded in the "about:addons" page):
-          if (!tabForEvent || tabForEvent.selected)
-            return;
-
-          // We always switch tabs for beforeunload tab-modal prompts.
-          if (event.detail &&
-              event.detail.tabPrompt &&
-              !event.detail.inPermitUnload) {
-            let docPrincipal = targetIsWindow ? event.target.document.nodePrincipal : null;
-            // At least one of these should/will be non-null:
-            let promptPrincipal = event.detail.promptPrincipal || docPrincipal ||
-                                  tabForEvent.linkedBrowser.contentPrincipal;
-            // For null principals, we bail immediately and don't show the checkbox:
-            if (!promptPrincipal || promptPrincipal.isNullPrincipal) {
-              tabForEvent.setAttribute("attention", "true");
-              return;
-            }
-
-            // For non-system/expanded principals, we bail and show the checkbox
-            if (promptPrincipal.URI &&
-                !Services.scriptSecurityManager.isSystemPrincipal(promptPrincipal)) {
-              let permission = Services.perms.testPermissionFromPrincipal(promptPrincipal,
-                                                                          "focus-tab-by-prompt");
-              if (permission != Services.perms.ALLOW_ACTION) {
-                // Tell the prompt box we want to show the user a checkbox:
-                let tabPrompt = this.getTabModalPromptBox(tabForEvent.linkedBrowser);
-                tabPrompt.onNextPromptShowAllowFocusCheckboxFor(promptPrincipal);
-                tabForEvent.setAttribute("attention", "true");
-                return;
-              }
-            }
-            // ... so system and expanded principals, as well as permitted "normal"
-            // URI-based principals, always get to steal focus for the tab when prompting.
-          }
-
-          // If permissions/origins dictate so, bring tab to the front.
-          this.selectedTab = tabForEvent;
-        ]]>
-      </handler>
-      <handler event="DOMTitleChanged">
-        <![CDATA[
-          if (!event.isTrusted)
-            return;
-
-          var contentWin = event.target.defaultView;
-          if (contentWin != contentWin.top)
-            return;
-
-          var tab = this._getTabForContentWindow(contentWin);
-          if (!tab || tab.hasAttribute("pending"))
-            return;
-
-          var titleChanged = this.setTabTitle(tab);
-          if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
-            tab.setAttribute("titlechanged", "true");
-        ]]>
-      </handler>
-      <handler event="oop-browser-crashed">
-        <![CDATA[
-          if (!event.isTrusted)
-            return;
-
-          let browser = event.originalTarget;
-
-          // Preloaded browsers do not actually have any tabs. If one crashes,
-          // it should be released and removed.
-          if (browser === this._preloadedBrowser) {
-            this.removePreloadedBrowser();
-            return;
-          }
-
-          let icon = browser.mIconURL;
-          let tab = this.getTabForBrowser(browser);
-
-          if (this.selectedBrowser == browser) {
-            TabCrashHandler.onSelectedBrowserCrash(browser);
-          } else {
-            this.updateBrowserRemoteness(browser, false);
-            SessionStore.reviveCrashedTab(tab);
-          }
-
-          tab.removeAttribute("soundplaying");
-          this.setIcon(tab, icon, browser.contentPrincipal, browser.contentRequestContextID);
-        ]]>
-      </handler>
-      <handler event="DOMAudioPlaybackStarted">
-        <![CDATA[
-          var tab = this.getTabFromAudioEvent(event);
-          if (!tab) {
-            return;
-          }
-
-          clearTimeout(tab._soundPlayingAttrRemovalTimer);
-          tab._soundPlayingAttrRemovalTimer = 0;
-
-          let modifiedAttrs = [];
-          if (tab.hasAttribute("soundplaying-scheduledremoval")) {
-            tab.removeAttribute("soundplaying-scheduledremoval");
-            modifiedAttrs.push("soundplaying-scheduledremoval");
-          }
-
-          if (!tab.hasAttribute("soundplaying")) {
-            tab.setAttribute("soundplaying", true);
-            modifiedAttrs.push("soundplaying");
-          }
-
-          if (modifiedAttrs.length) {
-            // Flush style so that the opacity takes effect immediately, in
-            // case the media is stopped before the style flushes naturally.
-            getComputedStyle(tab).opacity;
-          }
-
-          this._tabAttrModified(tab, modifiedAttrs);
-        ]]>
-      </handler>
-      <handler event="DOMAudioPlaybackStopped">
-        <![CDATA[
-          var tab = this.getTabFromAudioEvent(event);
-          if (!tab) {
-            return;
-          }
-
-          if (tab.hasAttribute("soundplaying")) {
-            let removalDelay = Services.prefs.getIntPref("browser.tabs.delayHidingAudioPlayingIconMS");
-
-            tab.style.setProperty("--soundplaying-removal-delay", `${removalDelay - 300}ms`);
-            tab.setAttribute("soundplaying-scheduledremoval", "true");
-            this._tabAttrModified(tab, ["soundplaying-scheduledremoval"]);
-
-            tab._soundPlayingAttrRemovalTimer = setTimeout(() => {
-              tab.removeAttribute("soundplaying-scheduledremoval");
-              tab.removeAttribute("soundplaying");
-              this._tabAttrModified(tab, ["soundplaying", "soundplaying-scheduledremoval"]);
-            }, removalDelay);
-          }
-        ]]>
-      </handler>
-      <handler event="DOMAudioPlaybackBlockStarted">
-        <![CDATA[
-          var tab = this.getTabFromAudioEvent(event);
-          if (!tab) {
-            return;
-          }
-
-          if (!tab.hasAttribute("activemedia-blocked")) {
-            tab.setAttribute("activemedia-blocked", true);
-            this._tabAttrModified(tab, ["activemedia-blocked"]);
-            tab.startMediaBlockTimer();
-          }
-        ]]>
-      </handler>
-      <handler event="DOMAudioPlaybackBlockStopped">
-        <![CDATA[
-          var tab = this.getTabFromAudioEvent(event);
-          if (!tab) {
-            return;
-          }
-
-          if (tab.hasAttribute("activemedia-blocked")) {
-            tab.removeAttribute("activemedia-blocked");
-            this._tabAttrModified(tab, ["activemedia-blocked"]);
-            let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
-            hist.add(2 /* unblockByVisitingTab */);
-            tab.finishMediaBlockTimer();
-          }
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
-
   <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
     <implementation>
       <!-- Override scrollbox.xml method, since our scrollbox's children are
            inherited from the binding parent -->
       <method name="_getScrollableElements">
         <body><![CDATA[
           return Array.filter(document.getBindingParent(this).childNodes,
                               this._canScrollToElement, this);