Bug 1442651 - Remove the tabbrowser element and binding. r?bgrins draft
authorDão Gottwald <dao@mozilla.com>
Mon, 05 Mar 2018 20:33:36 +0100
changeset 763336 0bf77b231b1754ebed4c418f1d70a0cbdf616c54
parent 763332 3de1b81bf304f9fed7447186d45f53013c620042
push id101410
push userdgottwald@mozilla.com
push dateMon, 05 Mar 2018 19:34:07 +0000
reviewersbgrins
bugs1442651
milestone60.0a1
Bug 1442651 - Remove the tabbrowser element and binding. r?bgrins MozReview-Commit-ID: 2IXukkFq5C2
browser/base/content/browser-fullScreenAndPointerLock.js
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/global-scripts.inc
browser/base/content/tabbrowser.css
browser/base/content/tabbrowser.js
browser/base/content/tabbrowser.xml
browser/modules/AsyncTabSwitcher.jsm
browser/themes/osx/browser.css
browser/themes/shared/tabs.inc.css
testing/firefox-ui/tests/functional/safebrowsing/test_notification.py
toolkit/components/addoncompat/RemoteAddonsParent.jsm
toolkit/components/startup/tests/browser/browser_bug537449.js
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -365,24 +365,23 @@ var FullScreen = {
         this.willToggle(false);
         break;
       case "fullscreen":
         this.toggle();
         break;
       case "MozDOMFullscreen:Entered": {
         // The event target is the element which requested the DOM
         // fullscreen. If we were entering DOM fullscreen for a remote
-        // browser, the target would be `gBrowser` and the original
-        // target would be the browser which was the parameter of
+        // browser, the target would be the browser which was the parameter of
         // `remoteFrameFullscreenChanged` call. If the fullscreen
         // request was initiated from an in-process browser, we need
         // to get its corresponding browser here.
         let browser;
-        if (event.target == gBrowser.container) {
-          browser = event.originalTarget;
+        if (event.target.ownerGlobal == window) {
+          browser = event.target;
         } else {
           let topWin = event.target.ownerGlobal.top;
           browser = gBrowser.getBrowserForContentWindow(topWin);
         }
         TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
         this.enterDomFullscreen(browser);
         break;
       }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -111,24 +111,16 @@ panelview[mainview] > .panel-header {
   position: absolute;
 }
 
 .panel-viewstack {
   overflow: visible;
   transition: height var(--panelui-subview-transition-duration);
 }
 
-#navigator-toolbox {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#empty");
-}
-
-tabbrowser {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
-}
-
 #tabbrowser-tabs {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
 }
 
 @supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
   #tabbrowser-tabs {
     /* Without this, the tabs container width extends beyond the window width */
     width: 0;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -135,16 +135,24 @@ XPCOMUtils.defineLazyServiceGetters(this
 });
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                      "@mozilla.org/xre/app-info;1",
                                      "nsICrashReporter");
 }
 
+XPCOMUtils.defineLazyGetter(this, "gBrowser", function() {
+  // The TabBrowser class only exists in proper browser windows, whereas
+  // browser.js may be loaded in other windows where a non-tabbrowser
+  // browser might try to access the gBrowser property.
+  // eslint-disable-next-line no-undef
+  return (typeof TabBrowser == "function") ? new TabBrowser() : null;
+});
+
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/browser.properties");
 });
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   // This is a stringbundle-like interface to gBrowserBundle, formerly a getter for
   // the "bundle_browser" element.
   return {
     getString(key) {
@@ -218,17 +226,16 @@ XPCOMUtils.defineLazyGetter(this, "Win7F
       }
     };
   }
   return null;
 });
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
-var gBrowser = null; // Will be instantiated by the <tabbbrowser> constructor.
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
 var gMultiProcessBrowser =
   window.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIWebNavigation)
         .QueryInterface(Ci.nsILoadContext)
         .useRemoteTabs;
@@ -1297,17 +1304,18 @@ var gBrowserInit = {
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
     mm.loadFrameScript("chrome://global/content/content-HybridContentTelemetry.js", true);
     mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
-      Services.els.addSystemEventListener(gBrowser.container, "click", contentAreaClick, true);
+      Services.els.addSystemEventListener(gBrowser.mPanelContainer, "click",
+        contentAreaClick, true);
     }
 
     // hook up UI through progress listener
     gBrowser.addProgressListener(window.XULBrowserWindow);
     gBrowser.addTabsProgressListener(window.TabsProgressListener);
 
     SidebarUI.init();
 
@@ -1415,21 +1423,21 @@ var gBrowserInit = {
 
       let browser = gBrowser.getBrowserForDocument(event.target);
       // Reset the zoom for the tabcrashed page.
       ZoomManager.setZoomForBrowser(browser, 1);
     }, false, true);
 
     gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
       gIdentityHandler.refreshForInsecureLoginForms();
-    });
+    }, true);
 
     gBrowser.addEventListener("PermissionStateChange", function() {
       gIdentityHandler.refreshIdentityBlock();
-    });
+    }, true);
 
     // Get the service so that it initializes and registers listeners for new
     // tab pages in order to be ready for any early-loading about:newtab pages,
     // e.g., start/home page, command line / startup uris to load, sessionstore
     gAboutNewTabService.QueryInterface(Ci.nsISupports);
 
     this._handleURIToLoad();
 
@@ -1911,16 +1919,17 @@ var gBrowserInit = {
       BrowserOffline.uninit();
       IndexedDBPromptHelper.uninit();
       CanvasPermissionPromptHelper.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
 
     // Final window teardown, do this last.
+    gBrowser.destroy();
     window.XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIWebNavigation)
           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
           .XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -62,16 +62,17 @@
 
 # All JS files which are not content (only) dependent that browser.xul
 # wishes to include *must* go into the global-scripts.inc file
 # so that they can be shared by macBrowserOverlay.xul.
 #include global-scripts.inc
 
 <script type="application/javascript">
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
+  Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
 </script>
 
 # All sets except for popupsets (commands, keys, stringbundles and broadcasters) *must* go into the
 # browser-sets.inc file for sharing with hiddenWindow.xul.
 #define FULL_BROWSER_WINDOW
 #include browser-sets.inc
 #undef FULL_BROWSER_WINDOW
 
@@ -1206,22 +1207,38 @@
         </sidebarheader>
         <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true" disablefullscreen="true"
                   style="min-width: 14em; width: 18em; max-width: 36em;" tooltip="aHTMLTooltip"/>
       </vbox>
 
       <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
       <vbox id="appcontent" flex="1">
         <notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
-        <tabbrowser flex="1" contenttooltip="aHTMLTooltip"
-                    tabcontainer="tabbrowser-tabs"
-                    contentcontextmenu="contentAreaContextMenu"
-                    autocompletepopup="PopupAutoComplete"
-                    selectmenulist="ContentSelectDropdown"
-                    datetimepicker="DateTimePickerPanel"/>
+        <tabbox id="tabbrowser-tabbox"
+                    flex="1" eventnode="document" tabcontainer="tabbrowser-tabs"
+                    onselect="if (event.target.localName == 'tabpanels') gBrowser.updateCurrentBrowser();">
+          <tabpanels flex="1" class="plain" selectedIndex="0" id="tabbrowser-tabpanels">
+            <notificationbox flex="1" notificationside="top">
+              <hbox flex="1" class="browserSidebarContainer">
+                <vbox flex="1" class="browserContainer">
+                  <stack flex="1" class="browserStack">
+                    <browser id="tabbrowser-initialBrowser" type="content"
+                             message="true" messagemanagergroup="browsers"
+                             primary="true" blank="true"
+                             tooltip="aHTMLTooltip"
+                             contextmenu="contentAreaContextMenu"
+                             autocompletepopup="PopupAutoComplete"
+                             selectmenulist="ContentSelectDropdown"
+                             datetimepicker="DateTimePickerPanel"/>
+                  </stack>
+                </vbox>
+              </hbox>
+            </notificationbox>
+          </tabpanels>
+        </tabbox>
       </vbox>
       <vbox id="browser-border-end" hidden="true" layer="true"/>
     </hbox>
 #include ../../components/customizableui/content/customizeMode.inc.xul
   </deck>
 
   <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
     <html:div class="pointerlockfswarning-domain-text">
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -6,17 +6,16 @@
 # If you update this list, you may need to add a mapping within the following
 # file so that ESLint works correctly:
 # tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
 
 <script type="application/javascript">
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 for (let script of [
-  "chrome://browser/content/tabbrowser.js",
   "chrome://browser/content/browser.js",
 
   "chrome://browser/content/browser-captivePortal.js",
   "chrome://browser/content/browser-compacttheme.js",
   "chrome://browser/content/browser-feeds.js",
   "chrome://browser/content/browser-media.js",
   "chrome://browser/content/browser-pageActions.js",
   "chrome://browser/content/browser-places.js",
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -84,15 +84,15 @@ tabpanels {
   transition: width .15s ease-out;
 }
 
 browser[blank],
 browser[pendingpaint] {
   opacity: 0;
 }
 
-tabbrowser[pendingpaint] {
+#tabbrowser-tabpanels[pendingpaint] {
   background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
   background-repeat: no-repeat;
   background-position: center center;
   background-color: #f9f9f9 !important;
   background-size: 30px;
 }
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1,59 +1,43 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env mozilla/browser-window */
 
 class TabBrowser {
-  constructor(container) {
-    this.container = container;
+  constructor() {
     this.requiresAddonInterpositions = true;
 
-    // Pass along any used DOM methods to the container node. When this object turns
-    // into a custom element this won't be needed anymore.
-    this.addEventListener = this.container.addEventListener.bind(this.container);
-    this.removeEventListener = this.container.removeEventListener.bind(this.container);
-    this.dispatchEvent = this.container.dispatchEvent.bind(this.container);
-    this.getAttribute = this.container.getAttribute.bind(this.container);
-    this.hasAttribute = this.container.hasAttribute.bind(this.container);
-    this.setAttribute = this.container.setAttribute.bind(this.container);
-    this.removeAttribute = this.container.removeAttribute.bind(this.container);
-    this.appendChild = this.container.appendChild.bind(this.container);
-    this.ownerGlobal = this.container.ownerGlobal;
-    this.ownerDocument = this.container.ownerDocument;
-    this.namespaceURI = this.container.namespaceURI;
-    this.style = this.container.style;
-
     ChromeUtils.defineModuleGetter(this, "AsyncTabSwitcher",
       "resource:///modules/AsyncTabSwitcher.jsm");
 
     XPCOMUtils.defineLazyServiceGetters(this, {
       _unifiedComplete: ["@mozilla.org/autocomplete/search;1?name=unifiedcomplete", "mozIPlacesAutoComplete"],
       serializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"],
       mURIFixup: ["@mozilla.org/docshell/urifixup;1", "nsIURIFixup"],
     });
 
-    XPCOMUtils.defineLazyGetter(this, "initialBrowser", () => {
-      return document.getAnonymousElementByAttribute(this.container, "anonid", "initialBrowser");
-    });
-    XPCOMUtils.defineLazyGetter(this, "tabContainer", () => {
-      return document.getElementById(this.getAttribute("tabcontainer"));
-    });
-    XPCOMUtils.defineLazyGetter(this, "tabs", () => {
-      return this.tabContainer.childNodes;
-    });
-    XPCOMUtils.defineLazyGetter(this, "tabbox", () => {
-      return document.getAnonymousElementByAttribute(this.container, "anonid", "tabbox");
-    });
-    XPCOMUtils.defineLazyGetter(this, "mPanelContainer", () => {
-      return document.getAnonymousElementByAttribute(this.container, "anonid", "panelcontainer");
-    });
+    this.ownerGlobal = window;
+    this.ownerDocument = document;
+
+    this.mPanelContainer = document.getElementById("tabbrowser-tabpanels");
+    this.addEventListener = this.mPanelContainer.addEventListener.bind(this.mPanelContainer);
+    this.removeEventListener = this.mPanelContainer.removeEventListener.bind(this.mPanelContainer);
+    this.dispatchEvent = this.mPanelContainer.dispatchEvent.bind(this.mPanelContainer);
+
+    this.initialBrowser = document.getElementById("tabbrowser-initialBrowser");
+
+    this.tabContainer = document.getElementById("tabbrowser-tabs");
+
+    this.tabs = this.tabContainer.childNodes;
+
+    this.tabbox = document.getElementById("tabbrowser-tabbox");
 
     this.closingTabsEnum = { ALL: 0, OTHER: 1, TO_END: 2 };
 
     this._visibleTabs = null;
 
     this.mCurrentTab = null;
 
     this._lastRelatedTabMap = new WeakMap();
@@ -79,16 +63,18 @@ class TabBrowser {
     this._previewMode = false;
 
     this._lastFindValue = "";
 
     this._contentWaitingCount = 0;
 
     this.tabAnimationsInProgress = 0;
 
+    this._XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
     /**
      * Binding from browser to tab
      */
     this._tabForBrowser = new WeakMap();
 
     /**
      * 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
@@ -162,17 +148,17 @@ class TabBrowser {
     this._switcher = null;
 
     this._tabMinWidthLimit = 50;
 
     this._soundPlayingAttrRemovalTimer = 0;
 
     this._hoverTabTimer = null;
 
-    this.mCurrentBrowser = document.getAnonymousElementByAttribute(this.container, "anonid", "initialBrowser");
+    this.mCurrentBrowser = this.initialBrowser;
     this.mCurrentBrowser.permanentKey = {};
 
     CustomizableUI.addListener(this);
     this._updateNewTabVisibility();
 
     Services.obs.addObserver(this, "contextual-identity-updated");
 
     this.mCurrentTab = this.tabContainer.firstChild;
@@ -194,34 +180,36 @@ class TabBrowser {
     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);
+    document.getElementById("mainPopupSet").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 = new TabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
     const nsIWebProgress = Ci.nsIWebProgress;
     const filter = Cc["@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");
+    if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
+      this.mPanelContainer.style.backgroundColor = "-moz-default-background-color";
+    } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
+      this.mPanelContainer.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) {
@@ -288,18 +276,17 @@ class TabBrowser {
   }
 
   get popupAnchor() {
     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");
+    let popupAnchor = document.createElementNS(this._XUL_NS, "hbox");
     popupAnchor.className = "popup-anchor";
     popupAnchor.hidden = true;
     stack.appendChild(popupAnchor);
     return this.mCurrentTab._popupAnchor = popupAnchor;
   }
 
   set selectedTab(val) {
     if (gNavToolbox.collapsed && !this._allowTabChange) {
@@ -485,17 +472,17 @@ class TabBrowser {
 
   getFindBar(aTab) {
     if (!aTab)
       aTab = this.selectedTab;
 
     if (aTab._findBar)
       return aTab._findBar;
 
-    let findBar = document.createElementNS(this.namespaceURI, "findbar");
+    let findBar = document.createElementNS(this._XUL_NS, "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;
@@ -507,17 +494,17 @@ class TabBrowser {
     event.initEvent("TabFindInitialized", true, false);
     aTab.dispatchEvent(event);
 
     return findBar;
   }
 
   getStatusPanel() {
     if (!this._statusPanel) {
-      this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel");
+      this._statusPanel = document.createElementNS(this._XUL_NS, "statuspanel");
       this._statusPanel.setAttribute("inactive", "true");
       this._statusPanel.setAttribute("layer", "true");
       this._appendStatusPanel();
     }
     return this._statusPanel;
   }
 
   _appendStatusPanel() {
@@ -860,17 +847,17 @@ class TabBrowser {
   isFailedIcon(aURI) {
     if (!(aURI instanceof Ci.nsIURI))
       aURI = makeURI(aURI);
     return PlacesUtils.favicons.isFailedFavicon(aURI);
   }
 
   getWindowTitleForBrowser(aBrowser) {
     var newTitle = "";
-    var docElement = this.ownerDocument.documentElement;
+    var docElement = document.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.
@@ -902,17 +889,17 @@ class TabBrowser {
           newTitle = uri.prePath + sep + newTitle;
       }
     } catch (e) {}
 
     return newTitle;
   }
 
   updateTitlebar() {
-    this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
+    document.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
   }
 
   updateCurrentBrowser(aForceUpdate) {
     var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
     if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
       return;
 
     if (!aForceUpdate) {
@@ -1175,18 +1162,17 @@ class TabBrowser {
     // 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 prompts = newBrowser.parentNode.getElementsByTagNameNS(this._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.
@@ -1782,19 +1768,17 @@ class TabBrowser {
     // 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"));
-      }
+      browser.setAttribute("autocompletepopup", "PopupAutoComplete");
     }
 
     return browser;
   }
 
   _isPreloadingEnabled() {
     // Preloading for the newtab page is enabled when the pref is true
     // and the URL is "about:newtab". We do not support preloading for
@@ -1834,25 +1818,23 @@ class TabBrowser {
     FullZoom.onLocationChange(tabURI, false, browser);
   }
 
   _createBrowser(aParams) {
     // 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");
+    let b = document.createElementNS(this._XUL_NS, "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"));
+    b.setAttribute("contextmenu", "contentAreaContextMenu");
+    b.setAttribute("tooltip", "aHTMLTooltip");
 
     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;
@@ -1865,18 +1847,18 @@ class TabBrowser {
 
     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"));
+    if (!aParams.isPreloadBrowser) {
+      b.setAttribute("autocompletepopup", "PopupAutoComplete");
     }
 
     /*
      * 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".
@@ -1888,22 +1870,19 @@ class TabBrowser {
      *      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("selectmenulist", "ContentSelectDropdown");
+
+    b.setAttribute("datetimepicker", "DateTimePickerPanel");
 
     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.
@@ -1918,37 +1897,35 @@ class TabBrowser {
     // 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");
+    let stack = document.createElementNS(this._XUL_NS, "stack");
     stack.className = "browserStack";
     stack.appendChild(b);
     stack.setAttribute("flex", "1");
 
     // Create the browserContainer
-    var browserContainer = document.createElementNS(NS_XUL, "vbox");
+    let browserContainer = document.createElementNS(this._XUL_NS, "vbox");
     browserContainer.className = "browserContainer";
     browserContainer.appendChild(stack);
     browserContainer.setAttribute("flex", "1");
 
     // Create the sidebar container
-    var browserSidebarContainer = document.createElementNS(NS_XUL,
-      "hbox");
+    let browserSidebarContainer = document.createElementNS(this._XUL_NS, "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");
+    let notificationbox = document.createElementNS(this._XUL_NS, "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");
@@ -2173,17 +2150,16 @@ class TabBrowser {
     let evt = new CustomEvent("TabBrowserDiscarded", { bubbles: true });
     tab.dispatchEvent(evt);
   }
 
   // eslint-disable-next-line complexity
   addTab(aURI, aReferrerURI, aCharset, aPostData, aOwner, aAllowThirdPartyFixup) {
     "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;
@@ -2251,17 +2227,17 @@ class TabBrowser {
     // 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");
+    var t = document.createElementNS(this._XUL_NS, "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 */ }
@@ -3552,22 +3528,23 @@ class TabBrowser {
 
   moveTabToEnd() {
     var tabPos = this.mCurrentTab._tPos;
     if (tabPos < this.browsers.length - 1)
       this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
   }
 
   moveTabOver(aEvent) {
-    var direction = window.getComputedStyle(this.container.parentNode).direction;
+    let direction = window.getComputedStyle(document.documentElement).direction;
     if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
-      (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
+        (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT)) {
       this.moveTabForward();
-    else
+    } else {
       this.moveTabBackward();
+    }
   }
 
   /**
    * @param   aTab
    *          Can be from a different window as well
    * @param   aRestoreTabImmediately
    *          Can defer loading of the tab contents
    */
@@ -3665,17 +3642,17 @@ class TabBrowser {
       if (!aEvent.metaKey)
         return;
 
       var offset = 1;
       switch (aEvent.charCode) {
         case "}".charCodeAt(0):
           offset = -1;
         case "{".charCodeAt(0):
-          if (window.getComputedStyle(this.container).direction == "ltr")
+          if (window.getComputedStyle(document.documentElement).direction == "ltr")
             offset *= -1;
           this.tabContainer.advanceSelectedTab(offset, true);
           aEvent.preventDefault();
       }
     }
   }
 
   createTooltip(event) {
@@ -3968,17 +3945,17 @@ class TabBrowser {
                         .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);
   }
 
-  disconnectedCallback() {
+  destroy() {
     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,
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4,58 +4,16 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <bindings id="tabBrowserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <!--
-  This binding is bound to <toolbox id="navigator-toolbox"> so that
-  the #tabbrowser binding is initialized before the #tabs binding.
-  Remove after bug 1392352.
-  -->
-  <binding id="empty"/>
-
-  <binding id="tabbrowser">
-    <resources>
-      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
-    </resources>
-
-    <content>
-      <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
-                  flex="1" eventnode="document" xbl:inherits="tabcontainer"
-                  onselect="if (event.target.localName == 'tabpanels') gBrowser.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>
-      <constructor>
-        gBrowser = new TabBrowser(this);
-      </constructor>
-      <destructor>
-        gBrowser.disconnectedCallback();
-      </destructor>
-    </implementation>
-  </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);
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -378,27 +378,27 @@ class AsyncTabSwitcher {
 
     // 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.tabbrowser.mPanelContainer.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.tabbrowser.mPanelContainer.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;
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -668,17 +668,17 @@ html|input.urlbar-input {
 }
 
 .openintabs-menuitem {
   list-style-image: none;
 }
 
 /* ::::: tabbrowser ::::: */
 
-.tabbrowser-tabbox {
+#tabbrowser-tabbox {
   margin: 0;
 }
 
 %include ../shared/tabs.inc.css
 
 .tab-label {
   margin-top: 1px;
   margin-bottom: 0;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -26,22 +26,22 @@
   --tab-min-width: 76px;
   --tab-loading-fill: #0A84FF;
 }
 
 #tabbrowser-tabs:-moz-lwtheme {
   --tab-line-color: var(--lwt-accent-color);
 }
 
-tabbrowser {
+#tabbrowser-tabpanels {
   /* Value for --in-content-page-background in in-content/common.inc.css */
   background-color: #f9f9fa;
 }
 
-:root[privatebrowsingmode=temporary] tabbrowser {
+:root[privatebrowsingmode=temporary] #tabbrowser-tabpanels {
   /* Value for --in-content-page-background in aboutPrivateBrowsing.css */
   background-color: #25003e;
 }
 
 #tabbrowser-tabs,
 #tabbrowser-tabs > .tabbrowser-arrowscrollbox,
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
   min-height: var(--tab-min-height);
--- a/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py
+++ b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py
@@ -110,47 +110,47 @@ class TestSafeBrowsingNotificationBar(Pu
         # Clean up here since the permission gets set in this function
         self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing')
 
     # Check the not a forgery or attack button in the notification bar
     def check_not_badware_button(self, button_property, report_page):
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
             label = self.browser.localize_property(button_property)
-            button = (self.marionette.find_element(By.TAG_NAME, 'tabbrowser')
-                      .find_element('anon attribute', {'label': label}))
+            button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
+                      .find_element(By.CSS_SELECTOR, 'button[label="' + label + '"]'))
 
             self.browser.tabbar.open_tab(lambda _: button.click())
 
         Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: report_page in mn.get_url(),
             message='The expected safe-browsing report page has not been opened',
         )
 
         with self.marionette.using_context('chrome'):
             self.browser.tabbar.close_tab()
 
     def check_get_me_out_of_here_button(self):
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
             label = self.browser.localize_property('safebrowsing.getMeOutOfHereButton.label')
-            button = (self.marionette.find_element(By.TAG_NAME, 'tabbrowser')
-                      .find_element('anon attribute', {'label': label}))
+            button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
+                      .find_element(By.CSS_SELECTOR, 'button[label="' + label + '"]'))
             button.click()
 
         Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda mn: self.browser.default_homepage in mn.get_url(),
             message='The default home page has not been loaded',
         )
 
     def check_x_button(self):
         with self.marionette.using_context('chrome'):
             # TODO: update to use safe browsing notification bar class when bug 1139544 lands
-            button = (self.marionette.find_element(By.TAG_NAME, 'tabbrowser')
-                      .find_element('anon attribute', {'value': 'blocked-badware-page'})
+            button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
+                      .find_element(By.CSS_SELECTOR, 'notification[value=blocked-badware-page]')
                       .find_element('anon attribute',
                                     {'class': 'messageCloseButton close-icon tabbable'}))
             button.click()
 
             Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
                 expected.element_stale(button),
                 message='The notification bar has not been closed',
             )
--- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm
@@ -429,24 +429,25 @@ var EventTargetParent = {
     if (target instanceof Ci.nsIDOMXULElement) {
       if (target.localName == "browser") {
         return target;
       } else if (target.localName == "tab") {
         return target.linkedBrowser;
       }
 
       // Check if |target| is somewhere on the path from the
-      // <tabbrowser> up to the root element.
+      // tabbrowser tabbox.
       let window = target.ownerGlobal;
 
       // Some non-browser windows define gBrowser globals which are not elements
       // and can't be passed to target.contains().
-      if (window && window.gBrowser &&
-          window.gBrowser.container instanceof Ci.nsIDOMXULElement &&
-          target.contains(window.gBrowser.container)) {
+      if (window &&
+          window.gBrowser &&
+          window.gBrowser.tabbox &&
+          target.contains(window.gBrowser.tabbox)) {
         return window;
       }
     }
 
     if (target.ownerGlobal && target === target.ownerGlobal.gBrowser) {
       return target.ownerGlobal;
     }
 
--- a/toolkit/components/startup/tests/browser/browser_bug537449.js
+++ b/toolkit/components/startup/tests/browser/browser_bug537449.js
@@ -24,21 +24,23 @@ function test() {
     });
 
     Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
     ok(seenDialog, "Should have seen a prompt dialog");
     ok(!window.closed, "Shouldn't have closed the window");
 
     let win2 = window.openDialog(location, "", "chrome,all,dialog=no", "about:blank");
     ok(win2 != null, "Should have been able to open a new window");
-    win2.addEventListener("load", function() {
-      win2.close();
+    win2.addEventListener("load", () => {
+      executeSoon(() => {
+        win2.close();
 
-      // Leave the page the second time.
-      waitForOnBeforeUnloadDialog(browser, (btnLeave, btnStay) => {
-        btnLeave.click();
+        // Leave the page the second time.
+        waitForOnBeforeUnloadDialog(browser, (btnLeave, btnStay) => {
+          btnLeave.click();
+        });
+
+        gBrowser.removeTab(gBrowser.selectedTab);
+        finish();
       });
-
-      gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(finish);
     }, {once: true});
   });
 }