Bug 1482389, replace TreeBoxObject with XULTreeElement inherited from XULElement, r=peterv,paolo
☠☠ backed out by 9663d8100bdb ☠ ☠
authorNeil Deakin <neil@mozilla.com>
Tue, 04 Dec 2018 11:25:30 -0500
changeset 510177 bd20e420f257ee6f7a983e67f2fce72b6a3310f3
parent 510176 af06731b5203d12428e0e17d2ad8fc9302d93277
child 510178 c1cfc595892fe24cccb8989a850e6556be6a778f
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv, paolo
bugs1482389
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1482389, replace TreeBoxObject with XULTreeElement inherited from XULElement, r=peterv,paolo
accessible/base/nsCoreUtils.cpp
accessible/base/nsCoreUtils.h
accessible/tests/mochitest/actions/test_tree.xul
accessible/tests/mochitest/actions/test_treegrid.xul
accessible/tests/mochitest/events/test_tree.xul
accessible/tests/mochitest/hittest/test_zoom_tree.xul
accessible/tests/mochitest/tree/test_tree.xul
accessible/windows/msaa/XULTreeGridAccessibleWrap.h
accessible/xul/XULTreeAccessible.cpp
accessible/xul/XULTreeAccessible.h
accessible/xul/XULTreeGridAccessible.cpp
accessible/xul/XULTreeGridAccessible.h
browser/base/content/browser-places.js
browser/base/content/pageinfo/pageInfo.js
browser/base/content/test/general/browser_tab_dragdrop2_frame1.xul
browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js
browser/components/places/PlacesUIUtils.jsm
browser/components/places/content/tree.xml
browser/components/places/content/treeView.js
browser/components/places/tests/browser/browser_forgetthissite_single.js
browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
browser/components/places/tests/browser/browser_library_middleclick.js
browser/components/places/tests/browser/browser_library_warnOnOpen.js
browser/components/places/tests/browser/browser_remove_bookmarks.js
browser/components/places/tests/browser/browser_views_iconsupdate.js
browser/components/places/tests/browser/head.js
browser/components/places/tests/chrome/test_0_bug510634.xul
browser/components/preferences/in-content/search.js
browser/components/preferences/in-content/tests/browser_cert_export.js
browser/components/preferences/translation.js
browser/components/sessionstore/test/browser_590563.js
dom/base/Document.cpp
dom/bindings/BindingUtils.cpp
dom/chrome-webidl/XULTreeElement.webidl
dom/chrome-webidl/moz.build
dom/tests/mochitest/general/test_interfaces.js
dom/webidl/TreeBoxObject.webidl
dom/webidl/TreeColumns.webidl
dom/webidl/TreeView.webidl
dom/webidl/moz.build
dom/xul/XULTreeElement.cpp
dom/xul/XULTreeElement.h
dom/xul/moz.build
dom/xul/nsXULElement.cpp
dom/xul/nsXULPrototypeCache.cpp
layout/base/PresShell.cpp
layout/generic/nsFrameIdList.h
layout/xul/crashtests/365151.xul
layout/xul/nsXULTooltipListener.cpp
layout/xul/nsXULTooltipListener.h
layout/xul/tree/TreeBoxObject.cpp
layout/xul/tree/TreeBoxObject.h
layout/xul/tree/moz.build
layout/xul/tree/nsITreeBoxObject.idl
layout/xul/tree/nsITreeSelection.idl
layout/xul/tree/nsITreeView.idl
layout/xul/tree/nsTreeBodyFrame.cpp
layout/xul/tree/nsTreeBodyFrame.h
layout/xul/tree/nsTreeColFrame.cpp
layout/xul/tree/nsTreeColFrame.h
layout/xul/tree/nsTreeColumns.cpp
layout/xul/tree/nsTreeColumns.h
layout/xul/tree/nsTreeContentView.cpp
layout/xul/tree/nsTreeContentView.h
layout/xul/tree/nsTreeImageListener.cpp
layout/xul/tree/nsTreeSelection.cpp
layout/xul/tree/nsTreeSelection.h
security/manager/pki/nsASN1Tree.cpp
security/manager/pki/nsASN1Tree.h
security/manager/ssl/nsCertTree.cpp
security/manager/ssl/nsCertTree.h
toolkit/components/passwordmgr/content/passwordManager.js
toolkit/components/passwordmgr/test/browser/browser_passwordmgr_editing.js
toolkit/content/tests/chrome/test_contextmenu_list.xul
toolkit/content/tests/chrome/test_popup_tree.xul
toolkit/content/tests/chrome/test_tree_view.xul
toolkit/content/tests/chrome/window_panel.xul
toolkit/content/tests/widgets/tree_shared.js
toolkit/content/treeUtils.js
toolkit/content/widgets/tree.js
toolkit/content/widgets/tree.xml
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -27,17 +27,17 @@
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "nsView.h"
 #include "nsGkAtoms.h"
 
 #include "nsComponentManagerUtils.h"
 
-#include "nsITreeBoxObject.h"
+#include "XULTreeElement.h"
 #include "nsTreeColumns.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLLabelElement.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/Selection.h"
 
 using namespace mozilla;
 
@@ -58,36 +58,36 @@ bool nsCoreUtils::HasClickListener(nsICo
       aContent->GetExistingListenerManager();
 
   return listenerManager &&
          (listenerManager->HasListenersFor(nsGkAtoms::onclick) ||
           listenerManager->HasListenersFor(nsGkAtoms::onmousedown) ||
           listenerManager->HasListenersFor(nsGkAtoms::onmouseup));
 }
 
-void nsCoreUtils::DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj,
+void nsCoreUtils::DispatchClickEvent(XULTreeElement *aTree,
                                      int32_t aRowIndex, nsTreeColumn *aColumn,
                                      const nsAString &aPseudoElt) {
   RefPtr<dom::Element> tcElm;
-  aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
+  aTree->GetTreeBody(getter_AddRefs(tcElm));
   if (!tcElm) return;
 
   Document *document = tcElm->GetUncomposedDoc();
   if (!document) return;
 
   nsCOMPtr<nsIPresShell> presShell = document->GetShell();
   if (!presShell) return;
 
   // Ensure row is visible.
-  aTreeBoxObj->EnsureRowIsVisible(aRowIndex);
+  aTree->EnsureRowIsVisible(aRowIndex);
 
   // Calculate x and y coordinates.
   int32_t x = 0, y = 0, width = 0, height = 0;
-  nsresult rv = aTreeBoxObj->GetCoordsForCellItem(
-      aRowIndex, aColumn, aPseudoElt, &x, &y, &width, &height);
+  nsresult rv = aTree->GetCoordsForCellItem(aRowIndex, aColumn, aPseudoElt, &x,
+                                            &y, &width, &height);
   if (NS_FAILED(rv)) return;
 
   nsCOMPtr<nsIBoxObject> tcBoxObj =
       nsXULElement::FromNode(tcElm)->GetBoxObject(IgnoreErrors());
 
   int32_t tcX = 0;
   tcBoxObj->GetX(&tcX);
 
@@ -420,56 +420,51 @@ void nsCoreUtils::GetLanguageFor(nsICont
   while (walkUp && walkUp != aRootContent &&
          (!walkUp->IsElement() ||
           !walkUp->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
                                         aLanguage)))
     walkUp = walkUp->GetParent();
 }
 
 already_AddRefed<nsIBoxObject> nsCoreUtils::GetTreeBodyBoxObject(
-    nsITreeBoxObject *aTreeBoxObj) {
+    XULTreeElement *aTree) {
   RefPtr<dom::Element> tcElm;
-  aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
+  aTree->GetTreeBody(getter_AddRefs(tcElm));
   RefPtr<nsXULElement> tcXULElm = nsXULElement::FromNodeOrNull(tcElm);
   if (!tcXULElm) return nullptr;
 
   return tcXULElm->GetBoxObject(IgnoreErrors());
 }
 
-already_AddRefed<nsITreeBoxObject> nsCoreUtils::GetTreeBoxObject(
-    nsIContent *aContent) {
+XULTreeElement *nsCoreUtils::GetTree(nsIContent *aContent) {
   // Find DOMNode's parents recursively until reach the <tree> tag
   nsIContent *currentContent = aContent;
   while (currentContent) {
     if (currentContent->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) {
-      // We will get the nsITreeBoxObject from the tree node
-      RefPtr<nsXULElement> xulElement = nsXULElement::FromNode(currentContent);
-      nsCOMPtr<nsIBoxObject> box = xulElement->GetBoxObject(IgnoreErrors());
-      nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
-      if (treeBox) return treeBox.forget();
+      return XULTreeElement::FromNode(currentContent);
     }
     currentContent = currentContent->GetFlattenedTreeParent();
   }
 
   return nullptr;
 }
 
 already_AddRefed<nsTreeColumn> nsCoreUtils::GetFirstSensibleColumn(
-    nsITreeBoxObject *aTree) {
+    XULTreeElement *aTree) {
   RefPtr<nsTreeColumns> cols;
   aTree->GetColumns(getter_AddRefs(cols));
   if (!cols) return nullptr;
 
   RefPtr<nsTreeColumn> column = cols->GetFirstColumn();
   if (column && IsColumnHidden(column)) return GetNextSensibleColumn(column);
 
   return column.forget();
 }
 
-uint32_t nsCoreUtils::GetSensibleColumnCount(nsITreeBoxObject *aTree) {
+uint32_t nsCoreUtils::GetSensibleColumnCount(XULTreeElement *aTree) {
   uint32_t count = 0;
 
   RefPtr<nsTreeColumns> cols;
   aTree->GetColumns(getter_AddRefs(cols));
   if (!cols) return count;
 
   nsTreeColumn *column = cols->GetFirstColumn();
 
@@ -478,17 +473,17 @@ uint32_t nsCoreUtils::GetSensibleColumnC
 
     column = column->GetNext();
   }
 
   return count;
 }
 
 already_AddRefed<nsTreeColumn> nsCoreUtils::GetSensibleColumnAt(
-    nsITreeBoxObject *aTree, uint32_t aIndex) {
+    XULTreeElement *aTree, uint32_t aIndex) {
   uint32_t idx = aIndex;
 
   nsCOMPtr<nsTreeColumn> column = GetFirstSensibleColumn(aTree);
   while (column) {
     if (idx == 0) return column.forget();
 
     idx--;
     column = GetNextSensibleColumn(column);
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -16,19 +16,24 @@
 #include "nsPoint.h"
 #include "nsTArray.h"
 
 class nsRange;
 class nsTreeColumn;
 class nsIBoxObject;
 class nsIFrame;
 class nsIDocShell;
-class nsITreeBoxObject;
 class nsIWidget;
 
+namespace mozilla {
+namespace dom {
+class XULTreeElement;
+}
+}  // namespace mozilla
+
 /**
  * Core utils.
  */
 class nsCoreUtils {
  public:
   typedef mozilla::dom::Document Document;
 
   /**
@@ -40,23 +45,23 @@ class nsCoreUtils {
    * Return true if the given node has registered click, mousedown or mouseup
    * event listeners.
    */
   static bool HasClickListener(nsIContent *aContent);
 
   /**
    * Dispatch click event to XUL tree cell.
    *
-   * @param  aTreeBoxObj  [in] tree box object
+   * @param  aTree        [in] tree
    * @param  aRowIndex    [in] row index
    * @param  aColumn      [in] column object
-   * @param  aPseudoElm   [in] pseudo elemenet inside the cell, see
+   * @param  aPseudoElm   [in] pseudo element inside the cell, see
    *                       nsITreeBoxObject for available values
    */
-  static void DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj,
+  static void DispatchClickEvent(mozilla::dom::XULTreeElement *aTree,
                                  int32_t aRowIndex, nsTreeColumn *aColumn,
                                  const nsAString &aPseudoElt = EmptyString());
 
   /**
    * Send mouse event to the given element.
    *
    * @param aMessage     [in] an event message (see EventForwards.h)
    * @param aX           [in] x coordinate in dev pixels
@@ -232,43 +237,42 @@ class nsCoreUtils {
    * @param aContent     [in] the given node
    * @param aRootContent [in] container of the given node
    * @param aLanguage    [out] language
    */
   static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
                              nsAString &aLanguage);
 
   /**
-   * Return box object for XUL treechildren element by tree box object.
+   * Return box object for XUL treechildren element of the given tree.
    */
   static already_AddRefed<nsIBoxObject> GetTreeBodyBoxObject(
-      nsITreeBoxObject *aTreeBoxObj);
+      mozilla::dom::XULTreeElement *aTree);
 
   /**
-   * Return tree box object from any levels DOMNode under the XUL tree.
+   * Return tree from any levels DOMNode under the XUL tree.
    */
-  static already_AddRefed<nsITreeBoxObject> GetTreeBoxObject(
-      nsIContent *aContent);
+  static mozilla::dom::XULTreeElement *GetTree(nsIContent *aContent);
 
   /**
    * Return first sensible column for the given tree box object.
    */
   static already_AddRefed<nsTreeColumn> GetFirstSensibleColumn(
-      nsITreeBoxObject *aTree);
+      mozilla::dom::XULTreeElement *aTree);
 
   /**
    * Return sensible columns count for the given tree box object.
    */
-  static uint32_t GetSensibleColumnCount(nsITreeBoxObject *aTree);
+  static uint32_t GetSensibleColumnCount(mozilla::dom::XULTreeElement *aTree);
 
   /**
    * Return sensible column at the given index for the given tree box object.
    */
   static already_AddRefed<nsTreeColumn> GetSensibleColumnAt(
-      nsITreeBoxObject *aTree, uint32_t aIndex);
+      mozilla::dom::XULTreeElement *aTree, uint32_t aIndex);
 
   /**
    * Return next sensible column for the given column.
    */
   static already_AddRefed<nsTreeColumn> GetNextSensibleColumn(
       nsTreeColumn *aColumn);
 
   /**
--- a/accessible/tests/mochitest/actions/test_tree.xul
+++ b/accessible/tests/mochitest/actions/test_tree.xul
@@ -44,17 +44,17 @@
 
     // gA11yEventDumpID = "debug";
     //gA11yEventDumpToConsole = true; // debug
 
     function doTest()
     {
       var treeNode = getNode("tree");
 
-      var treeBodyNode = treeNode.boxObject.treeBody;
+      var treeBodyNode = treeNode.treeBody;
 
       var tree = getAccessible(treeNode);
       var expandedTreeItem = tree.getChildAt(2);
       var collapsedTreeItem = tree.getChildAt(5);
 
       var actions = [
         {
           ID: expandedTreeItem,
--- a/accessible/tests/mochitest/actions/test_treegrid.xul
+++ b/accessible/tests/mochitest/actions/test_treegrid.xul
@@ -64,17 +64,17 @@
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTestActions()
     {
       var treeNode = getNode("tabletree");
 
-      var treeBodyNode = treeNode.boxObject.treeBody;
+      var treeBodyNode = treeNode.treeBody;
       treeNode.focus();
 
       var tree = getAccessible(treeNode);
 
       var expandedTreeItem = tree.getChildAt(2);
       var collapsedTreeItem = tree.getChildAt(5);
       var cycleCell = expandedTreeItem.getChildAt(0);
       var checkableCell = expandedTreeItem.getChildAt(3);
--- a/accessible/tests/mochitest/events/test_tree.xul
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -152,17 +152,17 @@
 
     /**
      * Set tree view.
      */
     function setTreeView()
     {
       this.invoke = function setTreeView_invoke()
       {
-        gTreeBox.view = gView;
+        gTree.view = gView;
       }
 
       this.getID = function setTreeView_getID() { return "set tree view"; }
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, gTree)
       ];
     };
@@ -171,17 +171,17 @@
      * Insert row at 0 index and checks TreeRowCountChanged and TreeInvalidated
      * event.
      */
     function insertRow()
     {
       this.invoke = function insertRow_invoke()
       {
         gView.appendItem("last");
-        gTreeBox.rowCountChanged(0, 1);
+        gTree.rowCountChanged(0, 1);
       }
 
       this.eventSeq =
       [
         new rowCountChangedChecker("insertRow: ", 0, 1),
         new treeInvalidatedChecker("insertRow", 0, 5, null, null)
       ];
 
@@ -224,17 +224,17 @@
           walkDown = false;
         }
 
         // Fire 'TreeInvalidated' event by InvalidateColumn()
         var firstCol = gTree.columns.getFirstColumn();
         for (var i = 0; i < gView.rowCount; i++)
           gView.setCellText(i, firstCol, "hey " + String(i) + "x0");
 
-        gTreeBox.invalidateColumn(firstCol);
+        gTree.invalidateColumn(firstCol);
       }
 
       this.eventSeq =
       [
         new nameChangeChecker("invalidateColumn: ", 0, 0),
         new nameChangeChecker("invalidateColumn: ", 1, 0),
         new nameChangeChecker("invalidateColumn: ", 2, 0),
         new nameChangeChecker("invalidateColumn: ", 3, 0),
@@ -261,17 +261,17 @@
         // Fire 'TreeInvalidated' event by InvalidateRow()
         var colCount = gTree.columns.count;
         var column = gTree.columns.getFirstColumn();
         while (column) {
           gView.setCellText(1, column, "aloha 1x" + String(column.index));
           column = column.getNext();
         }
 
-        gTreeBox.invalidateRow(1);
+        gTree.invalidateRow(1);
       }
 
       this.eventSeq =
       [
         new nameChangeChecker("invalidateRow: ", 1, 0),
         new nameChangeChecker("invalidateRow: ", 1, 1),
         new rowNameChangeChecker("invalidateRow: ", 1),
         new treeInvalidatedChecker("invalidateRow", 1, 1, null, null)
@@ -282,28 +282,26 @@
         return "invalidate row";
       }
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     var gTree = null;
-    var gTreeBox = null;
     var gTreeView = null;
     var gQueue = null;
 
     // gA11yEventDumpID = "debug";
     gA11yEventDumpToConsole = true; // debuggin
 
     function doTest()
     {
       // Initialize the tree
       gTree = document.getElementById("tree");
-      gTreeBox = gTree.treeBoxObject;
       gView = new nsTableTreeView(5);
 
       // Perform actions
       gQueue = new eventQueue();
 
       gQueue.push(new setTreeView());
       gQueue.push(new insertRow());
       gQueue.push(new invalidateColumn());
--- a/accessible/tests/mochitest/hittest/test_zoom_tree.xul
+++ b/accessible/tests/mochitest/hittest/test_zoom_tree.xul
@@ -33,19 +33,18 @@
       var tree = tabDocument.getElementById("tree");
       var treecols = tabDocument.getElementById("treecols");
       var treecol1 = tabDocument.getElementById("treecol1");
 
       // tree columns
       hitTest(tree, treecols, treecol1);
 
       // tree rows and cells
-      var treeBoxObject = tree.treeBoxObject;
-      var treeBodyBoxObj = tree.treeBoxObject.treeBody.boxObject;
-      var rect = treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
+      var treeBodyBoxObj = tree.treeBody.boxObject;
+      var rect = tree.getCoordsForCellItem(1, tree.columns[0], "cell");
 
       var treeAcc = getAccessible(tree, [nsIAccessibleTable]);
       var cellAcc = treeAcc.getCellAt(1, 0);
       var rowAcc = cellAcc.parent;
 
       var cssX = rect.x + treeBodyBoxObj.x;
       var cssY = rect.y + treeBodyBoxObj.y;
       var [x, y] = CSSToDevicePixels(tabWindow, cssX, cssY);
--- a/accessible/tests/mochitest/tree/test_tree.xul
+++ b/accessible/tests/mochitest/tree/test_tree.xul
@@ -66,19 +66,18 @@
 
       var accTreeForTree = {
         role: aRole,
         children: [
           accTreeForColumns
         ]
       };
 
-      var treeBoxObject = aTree.treeBoxObject;
       var view = aTree.view;
-      var columnCount = treeBoxObject.columns.count;
+      var columnCount = aTree.columns.count;
 
       for (var idx = 0; idx < columnCount; idx++)
         accTreeForColumns.children.push({ COLUMNHEADER: [ ] });
       if (!aTree.hasAttribute("hidecolumnpicker"))
         accTreeForColumns.children.push({ PUSHBUTTON: [ { MENUPOPUP: [] } ] });
 
       for (var idx = 0; idx < view.rowCount; idx++)
         accTreeForTree.children.push(getTreeItemAccTree(aRole, columnCount));
--- a/accessible/windows/msaa/XULTreeGridAccessibleWrap.h
+++ b/accessible/windows/msaa/XULTreeGridAccessibleWrap.h
@@ -43,18 +43,19 @@ class XULTreeGridAccessibleWrap : public
  */
 class XULTreeGridCellAccessibleWrap : public XULTreeGridCellAccessible,
                                       public ia2AccessibleTableCell {
   ~XULTreeGridCellAccessibleWrap() {}
 
  public:
   XULTreeGridCellAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc,
                                 XULTreeGridRowAccessible* aRowAcc,
-                                nsITreeBoxObject* aTree, nsITreeView* aTreeView,
-                                int32_t aRow, nsTreeColumn* aColumn)
+                                dom::XULTreeElement* aTree,
+                                nsITreeView* aTreeView, int32_t aRow,
+                                nsTreeColumn* aColumn)
       : XULTreeGridCellAccessible(aContent, aDoc, aRowAcc, aTree, aTreeView,
                                   aRow, aColumn),
         ia2AccessibleTableCell(this) {}
 
   // IUnknown
   DECL_IUNKNOWN_INHERITED
 
   // nsISupports
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -43,17 +43,17 @@ XULTreeAccessible::XULTreeAccessible(nsI
     : AccessibleWrap(aContent, aDoc),
       mAccessibleCache(kDefaultTreeCacheLength) {
   mType = eXULTreeType;
   mGenericTypes |= eSelect;
 
   nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView();
   mTreeView = view;
 
-  mTree = nsCoreUtils::GetTreeBoxObject(aContent);
+  mTree = nsCoreUtils::GetTree(aContent);
   NS_ASSERTION(mTree, "Can't get mTree!\n");
 
   nsIContent* parentContent = mContent->GetParent();
   if (parentContent) {
     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
         do_QueryInterface(parentContent);
     if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
   }
@@ -582,17 +582,17 @@ already_AddRefed<Accessible> XULTreeAcce
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessibleBase
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeItemAccessibleBase::XULTreeItemAccessibleBase(
     nsIContent* aContent, DocAccessible* aDoc, Accessible* aParent,
-    nsITreeBoxObject* aTree, nsITreeView* aTreeView, int32_t aRow)
+    dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
     : AccessibleWrap(aContent, aDoc),
       mTree(aTree),
       mTreeView(aTreeView),
       mRow(aRow) {
   mParent = aParent;
   mStateFlags |= eSharedNode;
 }
 
@@ -901,17 +901,17 @@ void XULTreeItemAccessibleBase::GetCellN
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeItemAccessible::XULTreeItemAccessible(
     nsIContent* aContent, DocAccessible* aDoc, Accessible* aParent,
-    nsITreeBoxObject* aTree, nsITreeView* aTreeView, int32_t aRow)
+    dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
     : XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView,
                                 aRow) {
   mStateFlags |= eNoKidsFromDOM;
   mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree);
   GetCellName(mColumn, mCachedName);
 }
 
 XULTreeItemAccessible::~XULTreeItemAccessible() {}
@@ -982,17 +982,17 @@ XULTreeColumAccessible::XULTreeColumAcce
 
 Accessible* XULTreeColumAccessible::GetSiblingAtOffset(int32_t aOffset,
                                                        nsresult* aError) const {
   if (aOffset < 0)
     return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
 
   if (aError) *aError = NS_OK;  // fail peacefully
 
-  nsCOMPtr<nsITreeBoxObject> tree = nsCoreUtils::GetTreeBoxObject(mContent);
+  RefPtr<dom::XULTreeElement> tree = nsCoreUtils::GetTree(mContent);
   if (tree) {
     nsCOMPtr<nsITreeView> treeView;
     tree->GetView(getter_AddRefs(treeView));
     if (treeView) {
       int32_t rowCount = 0;
       treeView->GetRowCount(&rowCount);
       if (rowCount > 0 && aOffset <= rowCount) {
         XULTreeAccessible* treeAcc = Parent()->AsXULTree();
--- a/accessible/xul/XULTreeAccessible.h
+++ b/accessible/xul/XULTreeAccessible.h
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_XULTreeAccessible_h__
 #define mozilla_a11y_XULTreeAccessible_h__
 
-#include "nsITreeBoxObject.h"
 #include "nsITreeView.h"
 #include "XULListboxAccessible.h"
+#include "mozilla/dom/XULTreeElement.h"
 
 class nsTreeBodyFrame;
 class nsTreeColumn;
 
 namespace mozilla {
 namespace a11y {
 
 class XULTreeGridCellAccessible;
@@ -109,17 +109,17 @@ class XULTreeAccessible : public Accessi
   virtual ~XULTreeAccessible();
 
   /**
    * Creates tree item accessible for the given row index.
    */
   virtual already_AddRefed<Accessible> CreateTreeItemAccessible(
       int32_t aRow) const;
 
-  nsCOMPtr<nsITreeBoxObject> mTree;
+  RefPtr<dom::XULTreeElement> mTree;
   nsITreeView* mTreeView;
   mutable AccessibleHashtable mAccessibleCache;
 };
 
 /**
  * Base class for tree item accessibles.
  */
 
@@ -128,17 +128,17 @@ class XULTreeAccessible : public Accessi
     0x1ab79ae7, 0x766a, 0x443c, {                    \
       0x94, 0x0b, 0xb1, 0xe6, 0xb0, 0x83, 0x1d, 0xfc \
     }                                                \
   }
 
 class XULTreeItemAccessibleBase : public AccessibleWrap {
  public:
   XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc,
-                            Accessible* aParent, nsITreeBoxObject* aTree,
+                            Accessible* aParent, dom::XULTreeElement* aTree,
                             nsITreeView* aTreeView, int32_t aRow);
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessibleBase,
                                            AccessibleWrap)
 
   // Accessible
@@ -202,31 +202,31 @@ class XULTreeItemAccessibleBase : public
    */
   bool IsExpandable() const;
 
   /**
    * Return name for cell at the given column.
    */
   void GetCellName(nsTreeColumn* aColumn, nsAString& aName) const;
 
-  nsCOMPtr<nsITreeBoxObject> mTree;
+  RefPtr<dom::XULTreeElement> mTree;
   nsITreeView* mTreeView;
   int32_t mRow;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(XULTreeItemAccessibleBase,
                               XULTREEITEMBASEACCESSIBLE_IMPL_CID)
 
 /**
  * Accessible class for items for XUL tree.
  */
 class XULTreeItemAccessible : public XULTreeItemAccessibleBase {
  public:
   XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc,
-                        Accessible* aParent, nsITreeBoxObject* aTree,
+                        Accessible* aParent, dom::XULTreeElement* aTree,
                         nsITreeView* aTreeView, int32_t aRow);
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessible,
                                            XULTreeItemAccessibleBase)
 
   // Accessible
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -194,17 +194,17 @@ already_AddRefed<Accessible> XULTreeGrid
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridRowAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeGridRowAccessible::XULTreeGridRowAccessible(
     nsIContent* aContent, DocAccessible* aDoc, Accessible* aTreeAcc,
-    nsITreeBoxObject* aTree, nsITreeView* aTreeView, int32_t aRow)
+    dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
     : XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView,
                                 aRow),
       mAccessibleCache(kDefaultTreeCacheLength) {
   mGenericTypes |= eTableRow;
   mStateFlags |= eNoKidsFromDOM;
 }
 
 XULTreeGridRowAccessible::~XULTreeGridRowAccessible() {}
@@ -332,17 +332,17 @@ void XULTreeGridRowAccessible::RowInvali
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULTreeGridCellAccessible::XULTreeGridCellAccessible(
     nsIContent* aContent, DocAccessible* aDoc,
-    XULTreeGridRowAccessible* aRowAcc, nsITreeBoxObject* aTree,
+    XULTreeGridRowAccessible* aRowAcc, dom::XULTreeElement* aTree,
     nsITreeView* aTreeView, int32_t aRow, nsTreeColumn* aColumn)
     : LeafAccessible(aContent, aDoc),
       mTree(aTree),
       mTreeView(aTreeView),
       mRow(aRow),
       mColumn(aColumn) {
   mParent = aRowAcc;
   mStateFlags |= eSharedNode;
--- a/accessible/xul/XULTreeGridAccessible.h
+++ b/accessible/xul/XULTreeGridAccessible.h
@@ -65,17 +65,17 @@ class XULTreeGridAccessible : public XUL
  * Represents accessible for XUL tree item in the case when XUL tree has
  * multiple columns.
  */
 class XULTreeGridRowAccessible final : public XULTreeItemAccessibleBase {
  public:
   using Accessible::GetChildAt;
 
   XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc,
-                           Accessible* aParent, nsITreeBoxObject* aTree,
+                           Accessible* aParent, dom::XULTreeElement* aTree,
                            nsITreeView* aTreeView, int32_t aRow);
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridRowAccessible,
                                            XULTreeItemAccessibleBase)
 
   // Accessible
@@ -107,17 +107,17 @@ class XULTreeGridRowAccessible final : p
  * multiple columns.
  */
 
 class XULTreeGridCellAccessible : public LeafAccessible,
                                   public TableCellAccessible {
  public:
   XULTreeGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc,
                             XULTreeGridRowAccessible* aRowAcc,
-                            nsITreeBoxObject* aTree, nsITreeView* aTreeView,
+                            dom::XULTreeElement* aTree, nsITreeView* aTreeView,
                             int32_t aRow, nsTreeColumn* aColumn);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridCellAccessible,
                                            LeafAccessible)
 
   // Accessible
@@ -167,17 +167,17 @@ class XULTreeGridCellAccessible : public
 
   /**
    * Return true if value of cell can be modified.
    */
   bool IsEditable() const;
 
   enum { eAction_Click = 0 };
 
-  nsCOMPtr<nsITreeBoxObject> mTree;
+  RefPtr<dom::XULTreeElement> mTree;
   nsITreeView* mTreeView;
 
   int32_t mRow;
   RefPtr<nsTreeColumn> mColumn;
 
   nsString mCachedTextEquiv;
 };
 
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -781,22 +781,21 @@ var BookmarksEventHandler = {
 
   fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
     var node;
     var cropped = false;
     var targetURI;
 
     if (aDocument.tooltipNode.localName == "treechildren") {
       var tree = aDocument.tooltipNode.parentNode;
-      var tbo = tree.treeBoxObject;
-      var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
+      var cell = tree.getCellAt(aEvent.clientX, aEvent.clientY);
       if (cell.row == -1)
         return false;
       node = tree.view.nodeForTreeIndex(cell.row);
-      cropped = tbo.isCellCropped(cell.row, cell.col);
+      cropped = tree.isCellCropped(cell.row, cell.col);
     } else {
       // Check whether the tooltipNode is a Places node.
       // In such a case use it, otherwise check for targetURI attribute.
       var tooltipNode = aDocument.tooltipNode;
       if (tooltipNode._placesNode)
         node = tooltipNode._placesNode;
       else {
         // This is a static non-Places node.
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -577,20 +577,21 @@ function openURL(target) {
   window.open(url, "_blank", "chrome");
 }
 
 function onBeginLinkDrag(event, urlField, descField) {
   if (event.originalTarget.localName != "treechildren")
     return;
 
   var tree = event.target;
-  if (!("treeBoxObject" in tree))
+  if (tree.localName != "tree") {
     tree = tree.parentNode;
+  }
 
-  var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
+  var row = tree.getRowAt(event.clientX, event.clientY);
   if (row == -1)
     return;
 
   // Adding URL flavor
   var col = tree.columns[urlField];
   var url = tree.view.getCellText(row, col);
   col = tree.columns[descField];
   var desc = tree.view.getCellText(row, col);
@@ -988,17 +989,17 @@ function formatDate(datestr, unknown) {
 }
 
 function doCopy() {
   if (!gClipboardHelper)
     return;
 
   var elem = document.commandDispatcher.focusedElement;
 
-  if (elem && "treeBoxObject" in elem) {
+  if (elem && elem.localName == "tree") {
     var view = elem.view;
     var selection = view.selection;
     var text = [], tmp = "";
     var min = {}, max = {};
 
     var count = selection.getRangeCount();
 
     for (var i = 0; i < count; i++) {
@@ -1022,35 +1023,36 @@ function doSelectAllMedia() {
 
   if (tree)
     tree.view.selection.selectAll();
 }
 
 function doSelectAll() {
   var elem = document.commandDispatcher.focusedElement;
 
-  if (elem && "treeBoxObject" in elem)
+  if (elem && elem.localName == "tree") {
     elem.view.selection.selectAll();
+  }
 }
 
 function selectImage() {
   if (!gImageElement)
     return;
 
   var tree = document.getElementById("imagetree");
   for (var i = 0; i < tree.view.rowCount; i++) {
     // If the image row element is the image selected from the "View Image Info" context menu item.
     let image = gImageView.data[i][COL_IMAGE_NODE];
     if (!gImageView.data[i][COL_IMAGE_BG] &&
         gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
         gImageElement.width == image.width &&
         gImageElement.height == image.height &&
         gImageElement.imageText == image.imageText) {
       tree.view.selection.select(i);
-      tree.treeBoxObject.ensureRowIsVisible(i);
+      tree.ensureRowIsVisible(i);
       tree.focus();
       return;
     }
   }
 }
 
 function checkProtocol(img) {
   var url = img[COL_IMAGE_ADDRESS];
--- a/browser/base/content/test/general/browser_tab_dragdrop2_frame1.xul
+++ b/browser/base/content/test/general/browser_tab_dragdrop2_frame1.xul
@@ -96,21 +96,21 @@ function createPanel(attrs)
   return document.documentElement.appendChild(panel);
 }
 
 function checkTreeCoords()
 {
   var tree = $("tree");
   var treechildren = $("treechildren");
   tree.currentIndex = 0;
-  tree.treeBoxObject.scrollToRow(0);
-  synthesizeMouse(treechildren, 10, tree.treeBoxObject.rowHeight + 2, { });
+  tree.scrollToRow(0);
+  synthesizeMouse(treechildren, 10, tree.rowHeight + 2, { });
 
-  tree.treeBoxObject.scrollToRow(2);
-  synthesizeMouse(treechildren, 10, tree.treeBoxObject.rowHeight + 2, { });
+  tree.scrollToRow(2);
+  synthesizeMouse(treechildren, 10, tree.rowHeight + 2, { });
 }
 
 var tests = [
   {
     testname: "normal panel",
     attrs: { },
     test: function(panel) {
       panel.openPopupAtScreen(200, 210);
--- a/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js
@@ -16,17 +16,17 @@ function testFirstPartyDomain(pageInfo) 
       Assert.ok(!!tree, "should have imagetree element");
 
       // i=0: <img>
       // i=1: <video>
       // i=2: <audio>
       for (let i = 0; i < 3; i++) {
         info("imagetree select " + i);
         tree.view.selection.select(i);
-        tree.treeBoxObject.ensureRowIsVisible(i);
+        tree.ensureRowIsVisible(i);
         tree.focus();
 
         let preview = pageInfo.document.getElementById("thepreviewimage");
         info("preview.src=" + preview.src);
 
         // For <img>, we will query imgIRequest.imagePrincipal later, so we wait
         // for loadend event. For <audio> and <video>, so far we only can get
         // the triggeringprincipal attribute on the node, so we simply wait for
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -932,52 +932,51 @@ var PlacesUIUtils = {
   },
 
   onSidebarTreeClick(event) {
     // right-clicks are not handled here
     if (event.button == 2)
       return;
 
     let tree = event.target.parentNode;
-    let tbo = tree.treeBoxObject;
-    let cell = tbo.getCellAt(event.clientX, event.clientY);
+    let cell = tree.getCellAt(event.clientX, event.clientY);
     if (cell.row == -1 || cell.childElt == "twisty")
       return;
 
     // getCoordsForCellItem returns the x coordinate in logical coordinates
     // (i.e., starting from the left and right sides in LTR and RTL modes,
     // respectively.)  Therefore, we make sure to exclude the blank area
     // before the tree item icon (that is, to the left or right of it in
     // LTR and RTL modes, respectively) from the click target area.
     let win = tree.ownerGlobal;
-    let rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image");
+    let rect = tree.getCoordsForCellItem(cell.row, cell.col, "image");
     let isRTL = win.getComputedStyle(tree).direction == "rtl";
     let mouseInGutter = isRTL ? event.clientX > rect.x
                               : event.clientX < rect.x;
 
     let metaKey = AppConstants.platform === "macosx" ? event.metaKey
                                                      : event.ctrlKey;
     let modifKey = metaKey || event.shiftKey;
-    let isContainer = tbo.view.isContainer(cell.row);
+    let isContainer = tree.view.isContainer(cell.row);
     let openInTabs = isContainer &&
                      (event.button == 1 || (event.button == 0 && modifKey)) &&
                      PlacesUtils.hasChildURIs(tree.view.nodeForTreeIndex(cell.row));
 
     if (event.button == 0 && isContainer && !openInTabs) {
-      tbo.view.toggleOpenState(cell.row);
+      tree.view.toggleOpenState(cell.row);
     } else if (!mouseInGutter && openInTabs &&
                event.originalTarget.localName == "treechildren") {
-      tbo.view.selection.select(cell.row);
+      tree.view.selection.select(cell.row);
       this.openMultipleLinksInTabs(tree.selectedNode, event, tree);
     } else if (!mouseInGutter && !isContainer &&
                event.originalTarget.localName == "treechildren") {
       // Clear all other selection since we're loading a link now. We must
       // do this *before* attempting to load the link since openURL uses
       // selection as an indication of which link to load.
-      tbo.view.selection.select(cell.row);
+      tree.view.selection.select(cell.row);
       this.openNodeWithEvent(tree.selectedNode, event);
     }
   },
 
   onSidebarTreeKeyPress(event) {
     let node = event.target.selectedNode;
     if (node) {
       if (event.keyCode == event.DOM_VK_RETURN)
@@ -990,17 +989,17 @@ var PlacesUIUtils = {
    * hovered over.
    */
   onSidebarTreeMouseMove(event) {
     let treechildren = event.target;
     if (treechildren.localName != "treechildren")
       return;
 
     let tree = treechildren.parentNode;
-    let cell = tree.treeBoxObject.getCellAt(event.clientX, event.clientY);
+    let cell = tree.getCellAt(event.clientX, event.clientY);
 
     // cell.row is -1 when the mouse is hovering an empty area within the tree.
     // To avoid showing a URL from a previously hovered node for a currently
     // hovered non-url node, we must clear the moused-over URL in these cases.
     if (cell.row != -1) {
       let node = tree.view.nodeForTreeIndex(cell.row);
       if (PlacesUtils.nodeIsURI(node)) {
         this.setMouseoverURL(node.uri, tree.ownerGlobal);
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -19,17 +19,17 @@
         // Force an initial build.
         if (this.place)
           this.place = this.place;
       ]]></constructor>
 
       <destructor><![CDATA[
         // Break the treeviewer->result->treeviewer cycle.
         // Note: unsetting the result's viewer also unsets
-        // the viewer's reference to our treeBoxObject.
+        // the viewer's reference to our tree.
         var result = this.result;
         if (result) {
           result.root.containerOpen = false;
         }
 
         // Unregister the controllber before unlinking the view, otherwise it
         // may still try to update commands on a view with a null result.
         if (this._controller) {
@@ -51,23 +51,28 @@
                 onget="return this.getAttribute('disableUserActions') == 'true';"
                 onset="if (val) this.setAttribute('disableUserActions', 'true');
                        else this.removeAttribute('disableUserActions'); return val;"/>
 
       <!-- overriding -->
       <property name="view">
         <getter><![CDATA[
           try {
-            return this.treeBoxObject.view.wrappedJSObject || null;
+            /* eslint-disable no-undef */
+            return Object.getOwnPropertyDescriptor(XULTreeElement.prototype, "view").get.
+                          call(this).wrappedJSObject || null;
+            /* eslint-enable no-undef */
           } catch (e) {
             return null;
           }
         ]]></getter>
         <setter><![CDATA[
-          return this.treeBoxObject.view = val;
+          /* eslint-disable no-undef */
+          return Object.getOwnPropertyDescriptor(XULTreeElement.prototype, "view").set.call(this, val);
+          /* eslint-enable no-undef */
         ]]></setter>
       </property>
 
       <property name="associatedElement"
                 readonly="true"
                 onget="return this"/>
 
       <method name="applyFilter">
@@ -119,17 +124,17 @@
             this._controller = new PlacesController(this);
             this._controller.disableUserActions = this.disableUserActions;
             this.controllers.appendController(this._controller);
           }
 
           let treeView = new PlacesTreeView(this.flatList, callback, this._controller);
 
           // Observer removal is done within the view itself.  When the tree
-          // goes away, treeboxobject calls view.setTree(null), which then
+          // goes away, view.setTree(null) is called, which then
           // calls removeObserver.
           result.addObserver(treeView);
           this.view = treeView;
 
           if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
             treeView.selection.select(0);
           }
 
@@ -259,17 +264,17 @@
           }
 
           let index = view.treeIndexForNode(node);
           if (index == -1)
             return;
 
           view.selection.select(index);
           // ... and ensure it's visible, not scrolled off somewhere.
-          this.treeBoxObject.ensureRowIsVisible(index);
+          this.ensureRowIsVisible(index);
         ]]></body>
       </method>
 
       <!-- nsIPlacesView -->
       <property name="result">
         <getter><![CDATA[
           try {
             return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
@@ -751,29 +756,28 @@
         this._controller.setDataTransfer(event);
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragover"><![CDATA[
         if (event.target.localName != "treechildren")
           return;
 
-        let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY);
+        let cell = this.getCellAt(event.clientX, event.clientY);
         let node = cell.row != -1 ?
                    this.view.nodeForTreeIndex(cell.row) :
                    this.result.root;
         // cache the dropTarget for the view
         PlacesControllerDragHelper.currentDropTarget = node;
 
         // We have to calculate the orientation since view.canDrop will use
         // it and we want to be consistent with the dropfeedback.
-        let tbo = this.treeBoxObject;
-        let rowHeight = tbo.rowHeight;
-        let eventY = event.clientY - tbo.treeBody.boxObject.y -
-                     rowHeight * (cell.row - tbo.getFirstVisibleRow());
+        let rowHeight = this.rowHeight;
+        let eventY = event.clientY - this.treeBody.boxObject.y -
+                     rowHeight * (cell.row - this.getFirstVisibleRow());
 
         let orientation = Ci.nsITreeView.DROP_BEFORE;
 
         if (cell.row == -1) {
           // If the row is not valid we try to insert inside the resultNode.
           orientation = Ci.nsITreeView.DROP_ON;
         } else if (PlacesUtils.nodeIsContainer(node) &&
                  eventY > rowHeight * 0.75) {
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -878,30 +878,30 @@ PlacesTreeView.prototype = {
 
   invalidateContainer: function PTV_invalidateContainer(aContainer) {
     console.assert(this._result, "Need to have a result to update");
     if (!this._tree)
       return;
 
     // If we are currently editing, don't invalidate the container until we
     // finish.
-    if (this._tree.element.getAttribute("editing")) {
+    if (this._tree.getAttribute("editing")) {
       if (!this._editingObservers) {
         this._editingObservers = new Map();
       }
       if (!this._editingObservers.has(aContainer)) {
         let mutationObserver = new MutationObserver(() => {
           Services.tm.dispatchToMainThread(
             () => this.invalidateContainer(aContainer));
           let observer = this._editingObservers.get(aContainer);
           observer.disconnect();
           this._editingObservers.delete(aContainer);
         });
 
-        mutationObserver.observe(this._tree.element, {
+        mutationObserver.observe(this._tree, {
           attributes: true,
           attributeFilter: ["editing"],
         });
 
         this._editingObservers.set(aContainer, mutationObserver);
       }
       return;
     }
@@ -1343,17 +1343,17 @@ PlacesTreeView.prototype = {
       return;
     }
 
     // We are responsible for translating the |index| and |orientation|
     // parameters into a container id and index within the container,
     // since this information is specific to the tree view.
     let ip = this._getInsertionPoint(aRow, aOrientation);
     if (ip) {
-      PlacesControllerDragHelper.onDrop(ip, aDataTransfer, this._tree.element)
+      PlacesControllerDragHelper.onDrop(ip, aDataTransfer, this._tree)
                                 .catch(Cu.reportError)
                                 .then(() => {
                                   // We should only clear the drop target once
                                   // the onDrop is complete, as it is an async function.
                                   PlacesControllerDragHelper.currentDropTarget = null;
                                 });
     }
   },
--- a/browser/components/places/tests/browser/browser_forgetthissite_single.js
+++ b/browser/components/places/tests/browser/browser_forgetthissite_single.js
@@ -45,17 +45,17 @@ var testForgetThisSiteVisibility = async
   selection.rangedSelect(0, selectionCount - 1, true);
   is(selection.count, selectionCount, "The selected range is as big as expected");
 
   // Open the context menu.
   let contextmenu = doc.getElementById("placesContext");
   let popupShown = promisePopupShown(contextmenu);
 
   // Get cell coordinates.
-  let rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text");
+  let rect = tree.getCoordsForCellItem(0, tree.columns[0], "text");
   // Initiate a context menu for the selected cell.
   EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {type: "contextmenu", button: 2}, organizer);
   await popupShown;
 
   let forgetThisSite = doc.getElementById("placesContext_deleteHost");
   let hideForgetThisSite = (selectionCount != 1);
   is(forgetThisSite.hidden, hideForgetThisSite,
     `The Forget this site menu item should ${hideForgetThisSite ? "" : "not "}` +
--- a/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
+++ b/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
@@ -78,17 +78,17 @@ add_task(async function test_open_folder
   for (let tab of tabs) {
     BrowserTestUtils.removeTab(tab);
   }
 });
 
 function mouseEventOnCell(aTree, aRowIndex, aColumnIndex, aEventDetails) {
   var selection = aTree.view.selection;
   selection.select(aRowIndex);
-  aTree.treeBoxObject.ensureRowIsVisible(aRowIndex);
+  aTree.ensureRowIsVisible(aRowIndex);
   var column = aTree.columns[aColumnIndex];
 
   // get cell coordinates
-  var rect = aTree.treeBoxObject.getCoordsForCellItem(aRowIndex, column, "text");
+  var rect = aTree.getCoordsForCellItem(aRowIndex, column, "text");
 
   EventUtils.synthesizeMouse(aTree.body, rect.x, rect.y,
                              aEventDetails, gLibrary);
 }
--- a/browser/components/places/tests/browser/browser_library_middleclick.js
+++ b/browser/components/places/tests/browser/browser_library_middleclick.js
@@ -195,17 +195,17 @@ add_task(async function test_all() {
   for (let test of gTests) {
     await runTest(test);
   }
 });
 
 function mouseEventOnCell(aTree, aRowIndex, aColumnIndex, aEventDetails) {
   var selection = aTree.view.selection;
   selection.select(aRowIndex);
-  aTree.treeBoxObject.ensureRowIsVisible(aRowIndex);
+  aTree.ensureRowIsVisible(aRowIndex);
   var column = aTree.columns[aColumnIndex];
 
   // get cell coordinates
-  var rect = aTree.treeBoxObject.getCoordsForCellItem(aRowIndex, column, "text");
+  var rect = aTree.getCoordsForCellItem(aRowIndex, column, "text");
 
   EventUtils.synthesizeMouse(aTree.body, rect.x, rect.y,
                              aEventDetails, gLibrary);
 }
--- a/browser/components/places/tests/browser/browser_library_warnOnOpen.js
+++ b/browser/components/places/tests/browser/browser_library_warnOnOpen.js
@@ -124,17 +124,17 @@ add_task(async function test_warnOnOpenL
   Assert.ok(true, "Expected dialog was shown when attempting to open lots of selected links");
 
   await PlacesUtils.bookmarks.eraseEverything();
 });
 
 function mouseEventOnCell(aTree, aRowIndex, aColumnIndex, aEventDetails) {
   var selection = aTree.view.selection;
   selection.select(aRowIndex);
-  aTree.treeBoxObject.ensureRowIsVisible(aRowIndex);
+  aTree.ensureRowIsVisible(aRowIndex);
   var column = aTree.columns[aColumnIndex];
 
   // get cell coordinates
-  var rect = aTree.treeBoxObject.getCoordsForCellItem(aRowIndex, column, "text");
+  var rect = aTree.getCoordsForCellItem(aRowIndex, column, "text");
 
   EventUtils.synthesizeMouse(aTree.body, rect.x, rect.y,
     aEventDetails, gLibrary);
 }
--- a/browser/components/places/tests/browser/browser_remove_bookmarks.js
+++ b/browser/components/places/tests/browser/browser_remove_bookmarks.js
@@ -91,19 +91,18 @@ add_task(async function test_remove_book
   Assert.equal(PlacesUtils.getConcreteItemGuid(PO._places.selectedNode),
     PlacesUtils.bookmarks.unfiledGuid, "Should have selected unfiled bookmarks.");
 
   let contextMenu = library.document.getElementById("placesContext");
   let contextMenuDeleteItem = library.document.getElementById("placesContext_delete");
 
   let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
 
-  let treeBoxObject = library.ContentTree.view.treeBoxObject;
   let firstColumn = library.ContentTree.view.columns[0];
-  let firstBookmarkRect = treeBoxObject.getCoordsForCellItem(0, firstColumn, "bm0");
+  let firstBookmarkRect = library.ContentTree.view.getCoordsForCellItem(0, firstColumn, "bm0");
 
   EventUtils.synthesizeMouse(
     library.ContentTree.view.body,
     firstBookmarkRect.x,
     firstBookmarkRect.y,
     { type: "contextmenu", button: 2 },
     library
   );
--- a/browser/components/places/tests/browser/browser_views_iconsupdate.js
+++ b/browser/components/places/tests/browser/browser_views_iconsupdate.js
@@ -104,18 +104,17 @@ function getNodeForToolbarItem(guid) {
  *        GUID of the item to search.
  * @returns DOM Node of the element.
  */
 async function getRectForSidebarItem(guid) {
   let sidebar = document.getElementById("sidebar");
   let tree = sidebar.contentDocument.getElementById("bookmarks-view");
   tree.selectItems([guid]);
   let treerect = tree.getBoundingClientRect();
-  let cellrect = tree.treeBoxObject.
-                      getCoordsForCellItem(tree.currentIndex, tree.columns[0], "cell");
+  let cellrect = tree.getCoordsForCellItem(tree.currentIndex, tree.columns[0], "cell");
 
   // Adjust the position for the tree and sidebar.
   return {
     left: treerect.left + cellrect.left + sidebar.getBoundingClientRect().left,
     top: treerect.top + cellrect.top + sidebar.getBoundingClientRect().top,
     width: cellrect.width,
     height: cellrect.height,
   };
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -61,26 +61,25 @@ function promiseLibraryClosed(organizer)
  */
 function promiseClipboard(aPopulateClipboardFn, aFlavor) {
   return new Promise((resolve, reject) => {
     waitForClipboard(data => !!data, aPopulateClipboardFn, resolve, reject, aFlavor);
   });
 }
 
 function synthesizeClickOnSelectedTreeCell(aTree, aOptions) {
-  let tbo = aTree.treeBoxObject;
-  if (tbo.view.selection.count < 1)
+  if (aTree.view.selection.count < 1)
      throw new Error("The test node should be successfully selected");
   // Get selection rowID.
   let min = {}, max = {};
-  tbo.view.selection.getRangeAt(0, min, max);
+  aTree.view.selection.getRangeAt(0, min, max);
   let rowID = min.value;
-  tbo.ensureRowIsVisible(rowID);
+  aTree.ensureRowIsVisible(rowID);
   // Calculate the click coordinates.
-  var rect = tbo.getCoordsForCellItem(rowID, aTree.columns[0], "text");
+  var rect = aTree.getCoordsForCellItem(rowID, aTree.columns[0], "text");
   var x = rect.x + rect.width / 2;
   var y = rect.y + rect.height / 2;
   // Simulate the click.
   EventUtils.synthesizeMouse(aTree.body, x, y, aOptions || {},
                              aTree.ownerGlobal);
 }
 
 /**
--- a/browser/components/places/tests/chrome/test_0_bug510634.xul
+++ b/browser/components/places/tests/chrome/test_0_bug510634.xul
@@ -43,17 +43,17 @@
     SimpleTest.waitForExplicitFinish();
 
     function runTest() {
       // Setup the places tree contents.
       let tree = document.getElementById("tree");
       tree.place = `place:type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_LEFT_PANE_QUERY}&excludeItems=1&expandQueries=0`;
 
       // The query-property is set on the title column for each row.
-      let titleColumn = tree.treeBoxObject.columns.getColumnAt(0);
+      let titleColumn = tree.columns.getColumnAt(0);
 
       // Open All Bookmarks
       tree.selectItems([PlacesUtils.virtualAllBookmarksGuid]);
       PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
       is(tree.selectedNode.uri,
          "place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY,
          "Opened All Bookmarks");
 
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -395,17 +395,17 @@ var gSearchPane = {
       document.getElementById("defaultEngine").selectedItem.engine;
     ExtensionSettingsStore.setByUser(SEARCH_TYPE, SEARCH_KEY);
   },
 };
 
 function onDragEngineStart(event) {
   var selectedIndex = gEngineView.selectedIndex;
   var tree = document.getElementById("engineList");
-  let cell = tree.treeBoxObject.getCellAt(event.clientX, event.clientY);
+  let cell = tree.getCellAt(event.clientX, event.clientY);
   if (selectedIndex >= 0 && !gEngineView.isCheckBox(cell.row, cell.col)) {
     event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
     event.dataTransfer.effectAllowed = "move";
   }
 }
 
 
 function EngineStore() {
--- a/browser/components/preferences/in-content/tests/browser_cert_export.js
+++ b/browser/components/preferences/in-content/tests/browser_cert_export.js
@@ -21,17 +21,17 @@ add_task(async function checkCertExportW
   let certButton = gBrowser.selectedBrowser.contentDocument.getElementById("viewCertificatesButton");
   certButton.scrollIntoView();
   let certDialogLoaded = promiseLoadSubDialog("chrome://pippki/content/certManager.xul");
   certButton.click();
   let dialogWin = await certDialogLoaded;
   let doc = dialogWin.document;
   doc.getElementById("certmanagertabs").selectedTab = doc.getElementById("ca_tab");
   let expectedCert;
-  let treeView = doc.getElementById("ca-tree").treeBoxObject.view;
+  let treeView = doc.getElementById("ca-tree").view;
   // Select any which cert. Ignore parent rows (ie rows without certs):
   for (let i = 0; i < treeView.rowCount; i++) {
     treeView.selection.select(i);
     dialogWin.getSelectedCerts();
     let certs = dialogWin.selected_certs; // yuck... but this is how the dialog works.
     if (certs && certs.length == 1 && certs[0]) {
       expectedCert = certs[0];
       // OK, we managed to select a cert!
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -13,18 +13,18 @@ const kLanguagesPref = "browser.translat
 
 function Tree(aId, aData) {
   this._data = aData;
   this._tree = document.getElementById(aId);
   this._tree.view = this;
 }
 
 Tree.prototype = {
-  get boxObject() {
-    return this._tree.treeBoxObject;
+  get tree() {
+    return this._tree;
   },
   get isEmpty() {
     return !this._data.length;
   },
   get hasSelection() {
     return this.selection.count > 0;
   },
   getSelectedItems() {
@@ -126,49 +126,49 @@ var gTranslationExceptions = {
   },
 
   observe(aSubject, aTopic, aData) {
     if (aTopic == "perm-changed") {
       if (aData == "cleared") {
         if (!this._sites.length)
           return;
         let removed = this._sites.splice(0, this._sites.length);
-        this._siteTree.boxObject.rowCountChanged(0, -removed.length);
+        this._siteTree.tree.rowCountChanged(0, -removed.length);
       } else {
         let perm = aSubject.QueryInterface(Ci.nsIPermission);
         if (perm.type != kPermissionType)
           return;
 
         if (aData == "added") {
           if (perm.capability != Services.perms.DENY_ACTION)
             return;
           this._sites.push(perm.principal.origin);
           this._sites.sort();
-          let boxObject = this._siteTree.boxObject;
-          boxObject.rowCountChanged(0, 1);
-          boxObject.invalidate();
+          let tree = this._siteTree.tree;
+          tree.rowCountChanged(0, 1);
+          tree.invalidate();
         } else if (aData == "deleted") {
           let index = this._sites.indexOf(perm.principal.origin);
           if (index == -1)
             return;
           this._sites.splice(index, 1);
-          this._siteTree.boxObject.rowCountChanged(index, -1);
+          this._siteTree.tree.rowCountChanged(index, -1);
           this.onSiteSelected();
           return;
         }
       }
       this.onSiteSelected();
     } else if (aTopic == "nsPref:changed") {
       this._langs = this.getLanguageExceptions();
       let change = this._langs.length - this._langTree.rowCount;
       this._langTree._data = this._langs;
-      let boxObject = this._langTree.boxObject;
+      let tree = this._langTree.tree;
       if (change)
-        boxObject.rowCountChanged(0, change);
-      boxObject.invalidate();
+        tree.rowCountChanged(0, change);
+      tree.invalidate();
       this.onLanguageSelected();
     }
   },
 
   _handleButtonDisabling(aTree, aIdPart) {
     let empty = aTree.isEmpty;
     document.getElementById("removeAll" + aIdPart + "s").disabled = empty;
     document.getElementById("remove" + aIdPart).disabled =
@@ -206,17 +206,17 @@ var gTranslationExceptions = {
     }
   },
 
   onAllSitesDeleted() {
     if (this._siteTree.isEmpty)
       return;
 
     let removedSites = this._sites.splice(0, this._sites.length);
-    this._siteTree.boxObject.rowCountChanged(0, -removedSites.length);
+    this._siteTree.tree.rowCountChanged(0, -removedSites.length);
 
     for (let origin of removedSites) {
       let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
       Services.perms.removeFromPrincipal(principal, kPermissionType);
     }
 
     this.onSiteSelected();
   },
--- a/browser/components/sessionstore/test/browser_590563.js
+++ b/browser/components/sessionstore/test/browser_590563.js
@@ -36,21 +36,21 @@ async function middleClickTest(win) {
   EventUtils.synthesizeMouseAtCenter(tabsToggle, { button: 0 }, browser.contentWindow);
   let treeContainer = browser.contentDocument.querySelector(".tree-container");
   await BrowserTestUtils.waitForCondition(() => win.getComputedStyle(treeContainer).visibility == "visible");
 
   let tree = browser.contentDocument.getElementById("tabList");
   is(tree.view.rowCount, 3, "There should be three items");
 
   // click on the first tab item
-  var rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[1], "text");
+  var rect = tree.getCoordsForCellItem(1, tree.columns[1], "text");
   EventUtils.synthesizeMouse(tree.body, rect.x, rect.y, { button: 1 },
                              browser.contentWindow);
   // click on the second tab item
-  rect = tree.treeBoxObject.getCoordsForCellItem(2, tree.columns[1], "text");
+  rect = tree.getCoordsForCellItem(2, tree.columns[1], "text");
   EventUtils.synthesizeMouse(tree.body, rect.x, rect.y, { button: 1 },
                              browser.contentWindow);
 
   is(win.gBrowser.tabs.length, 3,
      "The total number of tabs should be 3 after restoring 2 tabs by middle click.");
   is(win.gBrowser.visibleTabs.length, 3,
      "The total number of visible tabs should be 3 after restoring 2 tabs by middle click");
   finish();
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -262,23 +262,23 @@
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGDocument.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #ifdef MOZ_XUL
 #include "mozilla/dom/XULBroadcastManager.h"
 #include "mozilla/dom/XULPersist.h"
-#include "mozilla/dom/TreeBoxObject.h"
 #include "nsIXULWindow.h"
 #include "nsXULCommandDispatcher.h"
 #include "nsXULPopupManager.h"
 #include "nsIDocShellTreeOwner.h"
 #endif
 #include "nsIPresShellInlines.h"
+#include "mozilla/dom/BoxObject.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "mozilla/dom/WindowGlobalChild.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
@@ -5887,31 +5887,17 @@ already_AddRefed<BoxObject> Document::Ge
 
   RefPtr<BoxObject> boxObject;
   auto entry = mBoxObjectTable->LookupForAdd(aElement);
   if (entry) {
     boxObject = entry.Data();
     return boxObject.forget();
   }
 
-  int32_t namespaceID;
-  RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
-#ifdef MOZ_XUL
-  if (namespaceID == kNameSpaceID_XUL) {
-    if (tag == nsGkAtoms::tree) {
-      boxObject = new TreeBoxObject();
-    } else {
-      boxObject = new BoxObject();
-    }
-  } else
-#endif  // MOZ_XUL
-  {
-    boxObject = new BoxObject();
-  }
-
+  boxObject = new BoxObject();
   boxObject->Init(aElement);
   entry.OrInsert([&boxObject]() { return boxObject; });
 
   return boxObject.forget();
 }
 
 void Document::ClearBoxObjectFor(nsIContent* aContent) {
   if (mBoxObjectTable) {
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -55,16 +55,17 @@
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/ReportingUtils.h"
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/XULFrameElementBinding.h"
 #include "mozilla/dom/XULMenuElementBinding.h"
 #include "mozilla/dom/XULPopupElementBinding.h"
 #include "mozilla/dom/XULTextElementBinding.h"
+#include "mozilla/dom/XULTreeElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "mozilla/dom/XULScrollElementBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
@@ -3615,16 +3616,18 @@ bool HTMLConstructor(JSContext* aCx, uns
                definition->mLocalName == nsGkAtoms::browser ||
                definition->mLocalName == nsGkAtoms::editor) {
       cb = XULFrameElement_Binding::GetConstructorObject;
     } else if (definition->mLocalName == nsGkAtoms::menu ||
                definition->mLocalName == nsGkAtoms::menulist) {
       cb = XULMenuElement_Binding::GetConstructorObject;
     } else if (definition->mLocalName == nsGkAtoms::scrollbox) {
       cb = XULScrollElement_Binding::GetConstructorObject;
+    } else if (definition->mLocalName == nsGkAtoms::tree) {
+      cb = XULTreeElement_Binding::GetConstructorObject;
     } else {
       cb = XULElement_Binding::GetConstructorObject;
     }
   }
 
   int32_t tag = eHTMLTag_userdefined;
   if (!definition->IsCustomBuiltIn()) {
     // Step 4.
rename from dom/webidl/TreeBoxObject.webidl
rename to dom/chrome-webidl/XULTreeElement.webidl
--- a/dom/webidl/TreeBoxObject.webidl
+++ b/dom/chrome-webidl/XULTreeElement.webidl
@@ -8,19 +8,19 @@
 interface MozTreeView;
 
 dictionary TreeCellInfo {
     long row = 0;
     TreeColumn? col = null;
     DOMString childElt = "";
 };
 
-[NoInterfaceObject]
-interface TreeBoxObject : BoxObject {
-
+[HTMLConstructor, Func="IsChromeOrXBL"]
+interface XULTreeElement : XULElement
+{
   /**
    * Obtain the columns.
    */
   readonly attribute TreeColumns? columns;
 
   /**
    * The view that backs the tree and that supplies it with its data.
    * It is dynamically settable, either using a view attribute on the
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -53,17 +53,18 @@ WEBIDL_FILES = [
     'StructuredCloneHolder.webidl',
     'TelemetryStopwatch.webidl',
     'WebExtensionContentScript.webidl',
     'WebExtensionPolicy.webidl',
     'WindowGlobalActors.webidl',
     'XULFrameElement.webidl',
     'XULMenuElement.webidl',
     'XULScrollElement.webidl',
-    'XULTextElement.webidl'
+    'XULTextElement.webidl',
+    'XULTreeElement.webidl'
 ]
 
 if CONFIG['MOZ_PLACES']:
     WEBIDL_FILES += [
         'PlacesEvent.webidl',
         'PlacesObservers.webidl',
     ]
 
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -1279,16 +1279,18 @@ var interfaceNamesInGlobalScope =
     {name: "XULMenuElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULPopupElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULScrollElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULTextElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "XULTreeElement", insecureContext: true, xbl: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
 function createInterfaceMap(isXBLScope) {
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
--- a/dom/webidl/TreeColumns.webidl
+++ b/dom/webidl/TreeColumns.webidl
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 [Func="IsChromeOrXBL"]
 interface TreeColumns {
   /**
    * The tree widget for these columns.
    */
-  readonly attribute TreeBoxObject? tree;
+  readonly attribute XULTreeElement? tree;
 
   /**
    * The number of columns.
    */
   readonly attribute unsigned long count;
 
   /**
    * An alias for count (for the benefit of scripts which treat this as an
--- a/dom/webidl/TreeView.webidl
+++ b/dom/webidl/TreeView.webidl
@@ -135,17 +135,17 @@ interface TreeView
    */
   [Throws]
   DOMString getCellText(long row, TreeColumn column);
 
   /**
    * Called during initialization to link the view to the front end box object.
    */
   [Throws]
-  void setTree(TreeBoxObject? tree);
+  void setTree(XULTreeElement? tree);
 
   /**
    * Called on the view when an item is opened or closed.
    */
   [Throws]
   void toggleOpenState(long row);
 
   /**
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -892,17 +892,16 @@ WEBIDL_FILES = [
     'TextTrackCueList.webidl',
     'TextTrackList.webidl',
     'TimeEvent.webidl',
     'TimeRanges.webidl',
     'Touch.webidl',
     'TouchEvent.webidl',
     'TouchList.webidl',
     'TransitionEvent.webidl',
-    'TreeBoxObject.webidl',
     'TreeColumn.webidl',
     'TreeColumns.webidl',
     'TreeContentView.webidl',
     'TreeView.webidl',
     'TreeWalker.webidl',
     'U2F.webidl',
     'UDPMessageEvent.webidl',
     'UDPSocket.webidl',
rename from layout/xul/tree/TreeBoxObject.cpp
rename to dom/xul/XULTreeElement.cpp
--- a/layout/xul/tree/TreeBoxObject.cpp
+++ b/dom/xul/XULTreeElement.cpp
@@ -1,58 +1,67 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/TreeBoxObject.h"
 #include "nsCOMPtr.h"
-#include "nsXULElement.h"
 #include "nsTreeContentView.h"
 #include "nsITreeSelection.h"
 #include "ChildIterator.h"
 #include "nsError.h"
 #include "nsTreeBodyFrame.h"
-#include "mozilla/dom/TreeBoxObjectBinding.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/XULTreeElement.h"
+#include "mozilla/dom/XULTreeElementBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(TreeBoxObject, BoxObject, mView)
-
-NS_IMPL_ADDREF_INHERITED(TreeBoxObject, BoxObject)
-NS_IMPL_RELEASE_INHERITED(TreeBoxObject, BoxObject)
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeElement, nsXULElement)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeElement, nsXULElement, mView)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeBoxObject)
-  NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject)
-NS_INTERFACE_MAP_END_INHERITING(BoxObject)
+JSObject* XULTreeElement::WrapNode(JSContext* aCx,
+                                   JS::Handle<JSObject*> aGivenProto) {
+  return XULTreeElement_Binding::Wrap(aCx, this, aGivenProto);
+}
 
-void TreeBoxObject::Clear() {
-  ClearCachedValues();
-
+void XULTreeElement::UnbindFromTree(bool aDeep, bool aNullParent) {
   // Drop the view's ref to us.
   if (mView) {
     nsCOMPtr<nsITreeSelection> sel;
     mView->GetSelection(getter_AddRefs(sel));
-    if (sel) sel->SetTree(nullptr);
+    if (sel) {
+      sel->SetTree(nullptr);
+    }
     mView->SetTree(nullptr);  // Break the circular ref between the view and us.
   }
   mView = nullptr;
 
-  BoxObject::Clear();
+  nsXULElement::UnbindFromTree(aDeep, aNullParent);
 }
 
-TreeBoxObject::TreeBoxObject() : mTreeBody(nullptr) {}
+void XULTreeElement::DestroyContent() {
+  // Drop the view's ref to us.
+  if (mView) {
+    nsCOMPtr<nsITreeSelection> sel;
+    mView->GetSelection(getter_AddRefs(sel));
+    if (sel) {
+      sel->SetTree(nullptr);
+    }
+    mView->SetTree(nullptr);  // Break the circular ref between the view and us.
+  }
+  mView = nullptr;
 
-TreeBoxObject::~TreeBoxObject() {}
+  nsXULElement::DestroyContent();
+}
 
 static nsIContent* FindBodyElement(nsIContent* aParent) {
   mozilla::dom::FlattenedChildIterator iter(aParent);
   for (nsIContent* content = iter.GetNextChild(); content;
        content = iter.GetNextChild()) {
     mozilla::dom::NodeInfo* ni = content->NodeInfo();
     if (ni->Equals(nsGkAtoms::treechildren, kNameSpaceID_XUL)) {
       return content;
@@ -65,388 +74,349 @@ static nsIContent* FindBodyElement(nsICo
       nsIContent* result = FindBodyElement(content);
       if (result) return result;
     }
   }
 
   return nullptr;
 }
 
-nsTreeBodyFrame* TreeBoxObject::GetTreeBodyFrame(bool aFlushLayout) {
+nsTreeBodyFrame* XULTreeElement::GetTreeBodyFrame(bool aFlushLayout) {
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this;  // keep a reference
+  RefPtr<Document> doc = GetUncomposedDoc();
+
   // Make sure our frames are up to date, and layout as needed.  We
   // have to do this before checking for our cached mTreeBody, since
   // it might go away on style flush, and in any case if aFlushLayout
   // is true we need to make sure to flush no matter what.
   // XXXbz except that flushing style when we were not asked to flush
   // layout here breaks things.  See bug 585123.
-  nsIFrame* frame = nullptr;
-  if (aFlushLayout) {
-    frame = GetFrame(aFlushLayout);
-    if (!frame) return nullptr;
+  if (aFlushLayout && doc) {
+    doc->FlushPendingNotifications(FlushType::Layout);
   }
 
   if (mTreeBody) {
     // Have one cached already.
     return mTreeBody;
   }
 
-  if (!aFlushLayout) {
-    frame = GetFrame(aFlushLayout);
-    if (!frame) return nullptr;
+  if (!aFlushLayout && doc) {
+    doc->FlushPendingNotifications(FlushType::Frames);
   }
 
-  // Iterate over our content model children looking for the body.
-  nsCOMPtr<nsIContent> content = FindBodyElement(frame->GetContent());
-  if (!content) return nullptr;
+  nsCOMPtr<nsIContent> tree = FindBodyElement(this);
+  if (tree) {
+    mTreeBody = do_QueryFrame(tree->GetPrimaryFrame());
+  }
 
-  frame = content->GetPrimaryFrame();
-  if (!frame) return nullptr;
-
-  // Make sure that the treebodyframe has a pointer to |this|.
-  nsTreeBodyFrame* treeBody = do_QueryFrame(frame);
-  NS_ENSURE_TRUE(treeBody && treeBody->GetTreeBoxObject() == this, nullptr);
-
-  mTreeBody = treeBody;
   return mTreeBody;
 }
 
-NS_IMETHODIMP
-TreeBoxObject::GetView(nsITreeView** aView) {
+nsresult XULTreeElement::GetView(nsITreeView** aView) {
   if (!mTreeBody) {
     if (!GetTreeBodyFrame()) {
       // Don't return an uninitialised view
       *aView = nullptr;
       return NS_OK;
     }
 
-    if (mView)
+    if (mView) {
       // Our new frame needs to initialise itself
       return mTreeBody->GetView(aView);
+    }
   }
   if (!mView) {
-    RefPtr<nsXULElement> xulele = nsXULElement::FromNodeOrNull(mContent);
-    if (xulele) {
-      // No tree builder, create a tree content view.
-      nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView));
-      NS_ENSURE_SUCCESS(rv, rv);
+    // No tree builder, create a tree content view.
+    nsresult rv = NS_NewTreeContentView(getter_AddRefs(mView));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-      // Initialise the frame and view
-      mTreeBody->SetView(mView);
-    }
+    // Initialise the frame and view
+    mTreeBody->SetView(mView);
   }
   NS_IF_ADDREF(*aView = mView);
   return NS_OK;
 }
 
-already_AddRefed<nsITreeView> TreeBoxObject::GetView(CallerType /* unused */) {
+already_AddRefed<nsITreeView> XULTreeElement::GetView(CallerType /* unused */) {
   nsCOMPtr<nsITreeView> view;
   GetView(getter_AddRefs(view));
   return view.forget();
 }
 
-NS_IMETHODIMP
-TreeBoxObject::SetView(nsITreeView* aView) {
+nsresult XULTreeElement::SetView(nsITreeView* aView) {
   ErrorResult rv;
   SetView(aView, CallerType::System, rv);
   return rv.StealNSResult();
 }
 
-void TreeBoxObject::SetView(nsITreeView* aView, CallerType aCallerType,
-                            ErrorResult& aRv) {
+void XULTreeElement::SetView(nsITreeView* aView, CallerType aCallerType,
+                             ErrorResult& aRv) {
   if (aCallerType != CallerType::System) {
     // Don't trust views coming from random places.
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   mView = aView;
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) body->SetView(aView);
 }
 
-bool TreeBoxObject::Focused() {
+bool XULTreeElement::Focused() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->GetFocused();
   return false;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetFocused(bool* aFocused) {
+nsresult XULTreeElement::GetFocused(bool* aFocused) {
   *aFocused = Focused();
   return NS_OK;
 }
 
-NS_IMETHODIMP TreeBoxObject::SetFocused(bool aFocused) {
+void XULTreeElement::SetFocused(bool aFocused) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->SetFocused(aFocused);
-  return NS_OK;
+  if (body) body->SetFocused(aFocused);
 }
 
-NS_IMETHODIMP TreeBoxObject::GetTreeBody(Element** aElement) {
+nsresult XULTreeElement::GetTreeBody(Element** aElement) {
   *aElement = nullptr;
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->GetTreeBody(aElement);
   return NS_OK;
 }
 
-already_AddRefed<Element> TreeBoxObject::GetTreeBody() {
+already_AddRefed<Element> XULTreeElement::GetTreeBody() {
   RefPtr<Element> el;
   GetTreeBody(getter_AddRefs(el));
   return el.forget();
 }
 
-already_AddRefed<nsTreeColumns> TreeBoxObject::GetColumns() {
+already_AddRefed<nsTreeColumns> XULTreeElement::GetColumns() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->Columns();
   return nullptr;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetColumns(nsTreeColumns** aColumns) {
+nsresult XULTreeElement::GetColumns(nsTreeColumns** aColumns) {
   *aColumns = GetColumns().take();
   return NS_OK;
 }
 
-int32_t TreeBoxObject::RowHeight() {
+int32_t XULTreeElement::RowHeight() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->RowHeight();
   return 0;
 }
 
-int32_t TreeBoxObject::RowWidth() {
+int32_t XULTreeElement::RowWidth() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->RowWidth();
   return 0;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetRowHeight(int32_t* aRowHeight) {
+nsresult XULTreeElement::GetRowHeight(int32_t* aRowHeight) {
   *aRowHeight = RowHeight();
   return NS_OK;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetRowWidth(int32_t* aRowWidth) {
+nsresult XULTreeElement::GetRowWidth(int32_t* aRowWidth) {
   *aRowWidth = RowWidth();
   return NS_OK;
 }
 
-int32_t TreeBoxObject::GetFirstVisibleRow() {
+int32_t XULTreeElement::GetFirstVisibleRow() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->FirstVisibleRow();
   return 0;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetFirstVisibleRow(int32_t* aFirstVisibleRow) {
+nsresult XULTreeElement::GetFirstVisibleRow(int32_t* aFirstVisibleRow) {
   *aFirstVisibleRow = GetFirstVisibleRow();
   return NS_OK;
 }
 
-int32_t TreeBoxObject::GetLastVisibleRow() {
+int32_t XULTreeElement::GetLastVisibleRow() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->LastVisibleRow();
   return 0;
 }
 
-NS_IMETHODIMP TreeBoxObject::GetLastVisibleRow(int32_t* aLastVisibleRow) {
+nsresult XULTreeElement::GetLastVisibleRow(int32_t* aLastVisibleRow) {
   *aLastVisibleRow = GetLastVisibleRow();
   return NS_OK;
 }
 
-int32_t TreeBoxObject::HorizontalPosition() {
+int32_t XULTreeElement::HorizontalPosition() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->GetHorizontalPosition();
   return 0;
 }
 
-int32_t TreeBoxObject::GetPageLength() {
+int32_t XULTreeElement::GetPageLength() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->PageLength();
   return 0;
 }
 
-NS_IMETHODIMP
-TreeBoxObject::EnsureRowIsVisible(int32_t aRow) {
+void XULTreeElement::EnsureRowIsVisible(int32_t aRow) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->EnsureRowIsVisible(aRow);
-  return NS_OK;
+  if (body) body->EnsureRowIsVisible(aRow);
 }
 
-void TreeBoxObject::EnsureCellIsVisible(int32_t aRow, nsTreeColumn* aCol,
-                                        ErrorResult& aRv) {
+void XULTreeElement::EnsureCellIsVisible(int32_t aRow, nsTreeColumn* aCol,
+                                         ErrorResult& aRv) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) {
     nsresult rv = body->EnsureCellIsVisible(aRow, aCol);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
     }
   }
 }
 
-void TreeBoxObject::ScrollToRow(int32_t aRow) {
+void XULTreeElement::ScrollToRow(int32_t aRow) {
   nsTreeBodyFrame* body = GetTreeBodyFrame(true);
   if (!body) {
     return;
   }
 
   body->ScrollToRow(aRow);
 }
 
-void TreeBoxObject::ScrollByLines(int32_t aNumLines) {
+void XULTreeElement::ScrollByLines(int32_t aNumLines) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (!body) {
     return;
   }
   body->ScrollByLines(aNumLines);
 }
 
-void TreeBoxObject::ScrollByPages(int32_t aNumPages) {
+void XULTreeElement::ScrollByPages(int32_t aNumPages) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) body->ScrollByPages(aNumPages);
 }
 
-NS_IMETHODIMP TreeBoxObject::Invalidate() {
+void XULTreeElement::Invalidate() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->Invalidate();
-  return NS_OK;
+  if (body) body->Invalidate();
 }
 
-NS_IMETHODIMP
-TreeBoxObject::InvalidateColumn(nsTreeColumn* aCol) {
+void XULTreeElement::InvalidateColumn(nsTreeColumn* aCol) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->InvalidateColumn(aCol);
-  return NS_OK;
+  if (body) body->InvalidateColumn(aCol);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::InvalidateRow(int32_t aIndex) {
+void XULTreeElement::InvalidateRow(int32_t aIndex) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->InvalidateRow(aIndex);
-  return NS_OK;
+  if (body) body->InvalidateRow(aIndex);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::InvalidateCell(int32_t aRow, nsTreeColumn* aCol) {
+void XULTreeElement::InvalidateCell(int32_t aRow, nsTreeColumn* aCol) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->InvalidateCell(aRow, aCol);
-  return NS_OK;
+  if (body) body->InvalidateCell(aRow, aCol);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::InvalidateRange(int32_t aStart, int32_t aEnd) {
+void XULTreeElement::InvalidateRange(int32_t aStart, int32_t aEnd) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->InvalidateRange(aStart, aEnd);
-  return NS_OK;
+  if (body) body->InvalidateRange(aStart, aEnd);
 }
 
-int32_t TreeBoxObject::GetRowAt(int32_t x, int32_t y) {
+int32_t XULTreeElement::GetRowAt(int32_t x, int32_t y) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (!body) {
     return 0;
   }
   return body->GetRowAt(x, y);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow,
-                         nsTreeColumn** aCol, nsAString& aChildElt) {
+nsresult XULTreeElement::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow,
+                                   nsTreeColumn** aCol, nsAString& aChildElt) {
   *aRow = 0;
   *aCol = nullptr;
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) {
     nsAutoCString element;
     nsresult retval = body->GetCellAt(aX, aY, aRow, aCol, element);
     CopyUTF8toUTF16(element, aChildElt);
     return retval;
   }
   return NS_OK;
 }
 
-void TreeBoxObject::GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal,
-                              ErrorResult& aRv) {
+void XULTreeElement::GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal,
+                               ErrorResult& aRv) {
   GetCellAt(x, y, &aRetVal.mRow, getter_AddRefs(aRetVal.mCol),
             aRetVal.mChildElt);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::GetCoordsForCellItem(int32_t aRow, nsTreeColumn* aCol,
-                                    const nsAString& aElement, int32_t* aX,
-                                    int32_t* aY, int32_t* aWidth,
-                                    int32_t* aHeight) {
+nsresult XULTreeElement::GetCoordsForCellItem(int32_t aRow, nsTreeColumn* aCol,
+                                              const nsAString& aElement,
+                                              int32_t* aX, int32_t* aY,
+                                              int32_t* aWidth,
+                                              int32_t* aHeight) {
   *aX = *aY = *aWidth = *aHeight = 0;
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   NS_ConvertUTF16toUTF8 element(aElement);
   if (body)
     return body->GetCoordsForCellItem(aRow, aCol, element, aX, aY, aWidth,
                                       aHeight);
   return NS_OK;
 }
 
-already_AddRefed<DOMRect> TreeBoxObject::GetCoordsForCellItem(
+already_AddRefed<DOMRect> XULTreeElement::GetCoordsForCellItem(
     int32_t row, nsTreeColumn& col, const nsAString& element,
     ErrorResult& aRv) {
   int32_t x, y, w, h;
   GetCoordsForCellItem(row, &col, element, &x, &y, &w, &h);
-  RefPtr<DOMRect> rect = new DOMRect(mContent, x, y, w, h);
+  RefPtr<DOMRect> rect = new DOMRect(this, x, y, w, h);
   return rect.forget();
 }
 
-NS_IMETHODIMP
-TreeBoxObject::IsCellCropped(int32_t aRow, nsTreeColumn* aCol,
-                             bool* aIsCropped) {
+nsresult XULTreeElement::IsCellCropped(int32_t aRow, nsTreeColumn* aCol,
+                                       bool* aIsCropped) {
   *aIsCropped = false;
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) return body->IsCellCropped(aRow, aCol, aIsCropped);
   return NS_OK;
 }
 
-bool TreeBoxObject::IsCellCropped(int32_t row, nsTreeColumn* col,
-                                  ErrorResult& aRv) {
+bool XULTreeElement::IsCellCropped(int32_t row, nsTreeColumn* col,
+                                   ErrorResult& aRv) {
   bool ret;
   aRv = IsCellCropped(row, col, &ret);
   return ret;
 }
 
-NS_IMETHODIMP
-TreeBoxObject::RowCountChanged(int32_t aIndex, int32_t aDelta) {
+void XULTreeElement::RowCountChanged(int32_t aIndex, int32_t aDelta) {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->RowCountChanged(aIndex, aDelta);
-  return NS_OK;
+  if (body) body->RowCountChanged(aIndex, aDelta);
 }
 
-NS_IMETHODIMP
-TreeBoxObject::BeginUpdateBatch() {
+void XULTreeElement::BeginUpdateBatch() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->BeginUpdateBatch();
-  return NS_OK;
+  if (body) body->BeginUpdateBatch();
 }
 
-NS_IMETHODIMP
-TreeBoxObject::EndUpdateBatch() {
+void XULTreeElement::EndUpdateBatch() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->EndUpdateBatch();
-  return NS_OK;
+  if (body) body->EndUpdateBatch();
 }
 
-NS_IMETHODIMP
-TreeBoxObject::ClearStyleAndImageCaches() {
+void XULTreeElement::ClearStyleAndImageCaches() {
   nsTreeBodyFrame* body = GetTreeBodyFrame();
-  if (body) return body->ClearStyleAndImageCaches();
-  return NS_OK;
+  if (body) body->ClearStyleAndImageCaches();
 }
 
-void TreeBoxObject::RemoveImageCacheEntry(int32_t aRowIndex, nsTreeColumn& aCol,
-                                          ErrorResult& aRv) {
+void XULTreeElement::RemoveImageCacheEntry(int32_t aRowIndex,
+                                           nsTreeColumn& aCol,
+                                           ErrorResult& aRv) {
   if (NS_WARN_IF(aRowIndex < 0)) {
     aRv.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
   nsTreeBodyFrame* body = GetTreeBodyFrame();
   if (body) {
     body->RemoveImageCacheEntry(aRowIndex, &aCol);
   }
 }
 
-void TreeBoxObject::ClearCachedValues() { mTreeBody = nullptr; }
-
-JSObject* TreeBoxObject::WrapObject(JSContext* aCx,
-                                    JS::Handle<JSObject*> aGivenProto) {
-  return TreeBoxObject_Binding::Wrap(aCx, this, aGivenProto);
-}
-
 }  // namespace dom
 }  // namespace mozilla
rename from layout/xul/tree/TreeBoxObject.h
rename to dom/xul/XULTreeElement.h
--- a/layout/xul/tree/TreeBoxObject.h
+++ b/dom/xul/XULTreeElement.h
@@ -1,50 +1,50 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_TreeBoxObject_h
-#define mozilla_dom_TreeBoxObject_h
+#ifndef XULTreeElement_h__
+#define XULTreeElement_h__
 
-#include "mozilla/dom/BoxObject.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsString.h"
+#include "nsXULElement.h"
 #include "nsITreeView.h"
-#include "nsITreeBoxObject.h"
 
 class nsTreeBodyFrame;
 class nsTreeColumn;
 class nsTreeColumns;
 
 namespace mozilla {
 namespace dom {
 
 struct TreeCellInfo;
 class DOMRect;
 enum class CallerType : uint32_t;
 
-class TreeBoxObject final : public BoxObject, public nsITreeBoxObject {
+class XULTreeElement final : public nsXULElement {
  public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TreeBoxObject, BoxObject)
-  NS_DECL_NSITREEBOXOBJECT
-
-  TreeBoxObject();
+  explicit XULTreeElement(already_AddRefed<mozilla::dom::NodeInfo> &&aNodeInfo)
+      : nsXULElement(std::move(aNodeInfo)),
+        mCachedFirstVisibleRow(0),
+        mTreeBody(nullptr) {}
 
-  nsTreeBodyFrame* GetTreeBodyFrame(bool aFlushLayout = false);
-  nsTreeBodyFrame* GetCachedTreeBodyFrame() { return mTreeBody; }
+  NS_IMPL_FROMNODE_WITH_TAG(XULTreeElement, kNameSpaceID_XUL, tree)
 
-  // NS_PIBOXOBJECT interfaces
-  virtual void Clear() override;
-  virtual void ClearCachedValues() override;
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeElement, nsXULElement)
 
-  // WebIDL
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
+  nsTreeBodyFrame *GetTreeBodyFrame(bool aFlushLayout = false);
+  nsTreeBodyFrame *GetCachedTreeBodyFrame() { return mTreeBody; }
 
   already_AddRefed<nsTreeColumns> GetColumns();
 
   already_AddRefed<nsITreeView> GetView(CallerType /* unused */);
 
   void SetView(nsITreeView* arg, CallerType aCallerType, ErrorResult& aRv);
 
   bool Focused();
@@ -52,59 +52,87 @@ class TreeBoxObject final : public BoxOb
   already_AddRefed<Element> GetTreeBody();
 
   int32_t RowHeight();
 
   int32_t RowWidth();
 
   int32_t HorizontalPosition();
 
-  void EnsureCellIsVisible(int32_t row, nsTreeColumn* col, ErrorResult& aRv);
+  void EnsureCellIsVisible(int32_t row, nsTreeColumn *col, ErrorResult &aRv);
 
   void ScrollToRow(int32_t aRow);
 
   void ScrollByLines(int32_t aNumLines);
 
   void ScrollByPages(int32_t aNumPages);
 
   int32_t GetFirstVisibleRow();
 
   int32_t GetLastVisibleRow();
 
   int32_t GetPageLength();
 
   int32_t GetRowAt(int32_t x, int32_t y);
 
-  void GetCellAt(int32_t x, int32_t y, TreeCellInfo& aRetVal, ErrorResult& aRv);
+  void GetCellAt(int32_t x, int32_t y, TreeCellInfo &aRetVal, ErrorResult &aRv);
 
-  already_AddRefed<DOMRect> GetCoordsForCellItem(int32_t row, nsTreeColumn& col,
-                                                 const nsAString& element,
-                                                 ErrorResult& aRv);
+  already_AddRefed<DOMRect> GetCoordsForCellItem(int32_t row, nsTreeColumn &col,
+                                                 const nsAString &element,
+                                                 ErrorResult &aRv);
 
-  bool IsCellCropped(int32_t row, nsTreeColumn* col, ErrorResult& aRv);
+  bool IsCellCropped(int32_t row, nsTreeColumn *col, ErrorResult &aRv);
 
-  void RemoveImageCacheEntry(int32_t row, nsTreeColumn& col, ErrorResult& aRv);
+  void RemoveImageCacheEntry(int32_t row, nsTreeColumn &col, ErrorResult &aRv);
 
-  // Same signature (except for nsresult return type) as the XPIDL impls
-  // void Invalidate();
-  // void BeginUpdateBatch();
-  // void EndUpdateBatch();
-  // void ClearStyleAndImageCaches();
-  // void SetFocused(bool arg);
-  // void EnsureRowIsVisible(int32_t index);
-  // void InvalidateColumn(nsTreeColumn* col);
-  // void InvalidateRow(int32_t index);
-  // void InvalidateCell(int32_t row, nsTreeColumn* col);
-  // void InvalidateRange(int32_t startIndex, int32_t endIndex);
-  // void RowCountChanged(int32_t index, int32_t count);
+  void SetFocused(bool aFocused);
+  void EnsureRowIsVisible(int32_t index);
+  void Invalidate(void);
+  void InvalidateColumn(nsTreeColumn *col);
+  void InvalidateRow(int32_t index);
+  void InvalidateCell(int32_t row, nsTreeColumn *col);
+  void InvalidateRange(int32_t startIndex, int32_t endIndex);
+  void RowCountChanged(int32_t index, int32_t count);
+  void BeginUpdateBatch(void);
+  void EndUpdateBatch(void);
+  void ClearStyleAndImageCaches(void);
+  nsresult GetColumns(nsTreeColumns **aColumns);
+  nsresult GetView(nsITreeView **aView);
+  nsresult SetView(nsITreeView *aView);
+  nsresult GetFocused(bool *aFocused);
+  nsresult GetTreeBody(mozilla::dom::Element **aTreeBody);
+  nsresult GetRowHeight(int32_t *aRowHeight);
+  nsresult GetRowWidth(int32_t *aRowWidth);
+  nsresult GetFirstVisibleRow(int32_t *_retval);
+  nsresult GetLastVisibleRow(int32_t *_retval);
+  nsresult GetCellAt(int32_t x, int32_t y, int32_t *row, nsTreeColumn **col,
+                     nsAString &childElt);
+  nsresult GetCoordsForCellItem(int32_t row, nsTreeColumn *col,
+                                const nsAString &element, int32_t *x,
+                                int32_t *y, int32_t *width, int32_t *height);
+  nsresult IsCellCropped(int32_t row, nsTreeColumn *col, bool *_retval);
+
+  virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
+  virtual void DestroyContent() override;
+
+  void BodyDestroyed(int32_t aFirstVisibleRow) {
+    mTreeBody = nullptr;
+    mCachedFirstVisibleRow = aFirstVisibleRow;
+  }
+
+  int32_t GetCachedTopVisibleRow() { return mCachedFirstVisibleRow; }
 
  protected:
-  nsTreeBodyFrame* mTreeBody;
+  int32_t mCachedFirstVisibleRow;
+
+  nsTreeBodyFrame *mTreeBody;
   nsCOMPtr<nsITreeView> mView;
 
- private:
-  ~TreeBoxObject();
+  virtual ~XULTreeElement() {}
+
+  JSObject *WrapNode(JSContext *aCx,
+                     JS::Handle<JSObject *> aGivenProto) override;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -25,16 +25,17 @@ if CONFIG['MOZ_XUL']:
         'XULBroadcastManager.h',
         'XULFrameElement.h',
         'XULMenuElement.h',
         'XULPersist.h',
         'XULPopupElement.h',
         'XULScrollElement.h',
         'XULTextElement.h',
         'XULTooltipElement.h',
+        'XULTreeElement.h',
     ]
 
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULContentUtils.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
@@ -45,16 +46,17 @@ if CONFIG['MOZ_XUL']:
         'XULDocument.cpp',
         'XULFrameElement.cpp',
         'XULMenuElement.cpp',
         'XULPersist.cpp',
         'XULPopupElement.cpp',
         'XULScrollElement.cpp',
         'XULTextElement.cpp',
         'XULTooltipElement.cpp',
+        'XULTreeElement.cpp',
     ]
 
 XPIDL_SOURCES += [
     'nsIController.idl',
     'nsIControllers.idl',
 ]
 
 XPIDL_MODULE = 'xul'
@@ -68,14 +70,15 @@ LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/html',
     '/dom/xbl',
     '/dom/xml',
     '/layout/base',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
+    '/layout/xul/tree',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -73,16 +73,17 @@
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsICSSDeclaration.h"
 #include "nsLayoutUtils.h"
 #include "XULFrameElement.h"
 #include "XULMenuElement.h"
 #include "XULPopupElement.h"
 #include "XULScrollElement.h"
+#include "XULTreeElement.h"
 
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/BoxObject.h"
 #include "mozilla/dom/XULBroadcastManager.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/dom/XULCommandEvent.h"
 
@@ -165,16 +166,20 @@ nsXULElement* nsXULElement::Construct(
       nodeInfo->Equals(nsGkAtoms::menulist)) {
     return new XULMenuElement(nodeInfo.forget());
   }
 
   if (nodeInfo->Equals(nsGkAtoms::scrollbox)) {
     return new XULScrollElement(nodeInfo.forget());
   }
 
+  if (nodeInfo->Equals(nsGkAtoms::tree)) {
+    return new XULTreeElement(nodeInfo.forget());
+  }
+
   return NS_NewBasicXULElement(nodeInfo.forget());
 }
 
 /* static */
 already_AddRefed<nsXULElement> nsXULElement::CreateFromPrototype(
     nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo* aNodeInfo,
     bool aIsScriptable, bool aIsRoot) {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
--- a/dom/xul/nsXULPrototypeCache.cpp
+++ b/dom/xul/nsXULPrototypeCache.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXULPrototypeCache.h"
 
 #include "plstr.h"
 #include "nsXULPrototypeDocument.h"
 #include "nsIServiceManager.h"
 #include "nsIURI.h"
+#include "nsNetUtil.h"
 
 #include "nsIFile.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserverService.h"
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -131,25 +131,23 @@
 #endif
 
 // For style data reconstruction
 #include "nsStyleChangeList.h"
 #include "nsCSSFrameConstructor.h"
 #ifdef MOZ_XUL
 #include "nsMenuFrame.h"
 #include "nsTreeBodyFrame.h"
-#include "nsIBoxObject.h"
-#include "nsITreeBoxObject.h"
+#include "XULTreeElement.h"
 #include "nsMenuPopupFrame.h"
 #include "nsTreeColumns.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsXULElement.h"
-#include "mozilla/dom/BoxObject.h"
 #endif  // MOZ_XUL
 
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "ClientLayerManager.h"
 #include "GeckoProfiler.h"
 #include "gfxPlatform.h"
 #include "Layers.h"
 #include "LayerTreeInvalidation.h"
@@ -7972,51 +7970,47 @@ void PresShell::GetCurrentItemAndPositio
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
       aFocusedElement->AsXULMultiSelectControl();
   if (multiSelect) {
     checkLineHeight = false;
 
     int32_t currentIndex;
     multiSelect->GetCurrentIndex(&currentIndex);
     if (currentIndex >= 0) {
-      RefPtr<nsXULElement> xulElement = nsXULElement::FromNode(focusedContent);
-      if (xulElement) {
-        nsCOMPtr<nsIBoxObject> box = xulElement->GetBoxObject(IgnoreErrors());
-        nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
-        // Tree view special case (tree items have no frames)
-        // Get the focused row and add its coordinates, which are already in
-        // pixels
-        // XXX Boris, should we create a new interface so that this doesn't
-        // need to know about trees? Something like nsINodelessChildCreator
-        // which could provide the current focus coordinates?
-        if (treeBox) {
-          treeBox->EnsureRowIsVisible(currentIndex);
-          int32_t firstVisibleRow, rowHeight;
-          treeBox->GetFirstVisibleRow(&firstVisibleRow);
-          treeBox->GetRowHeight(&rowHeight);
-
-          extraTreeY += nsPresContext::CSSPixelsToAppUnits(
-              (currentIndex - firstVisibleRow + 1) * rowHeight);
-          istree = true;
-
-          RefPtr<nsTreeColumns> cols;
-          treeBox->GetColumns(getter_AddRefs(cols));
-          if (cols) {
-            nsTreeColumn* col = cols->GetFirstColumn();
-            if (col) {
-              RefPtr<Element> colElement = col->Element();
-              nsIFrame* frame = colElement->GetPrimaryFrame();
-              if (frame) {
-                extraTreeY += frame->GetSize().height;
-              }
+      RefPtr<XULTreeElement> tree = XULTreeElement::FromNode(focusedContent);
+      // Tree view special case (tree items have no frames)
+      // Get the focused row and add its coordinates, which are already in
+      // pixels
+      // XXX Boris, should we create a new interface so that this doesn't
+      // need to know about trees? Something like nsINodelessChildCreator
+      // which could provide the current focus coordinates?
+      if (tree) {
+        tree->EnsureRowIsVisible(currentIndex);
+        int32_t firstVisibleRow, rowHeight;
+        tree->GetFirstVisibleRow(&firstVisibleRow);
+        tree->GetRowHeight(&rowHeight);
+
+        extraTreeY += nsPresContext::CSSPixelsToAppUnits(
+            (currentIndex - firstVisibleRow + 1) * rowHeight);
+        istree = true;
+
+        RefPtr<nsTreeColumns> cols;
+        tree->GetColumns(getter_AddRefs(cols));
+        if (cols) {
+          nsTreeColumn* col = cols->GetFirstColumn();
+          if (col) {
+            RefPtr<Element> colElement = col->Element();
+            nsIFrame* frame = colElement->GetPrimaryFrame();
+            if (frame) {
+              extraTreeY += frame->GetSize().height;
             }
           }
-        } else {
-          multiSelect->GetCurrentItem(getter_AddRefs(item));
         }
+      } else {
+        multiSelect->GetCurrentItem(getter_AddRefs(item));
       }
     }
   } else {
     // don't check menulists as the selected item will be inside a popup.
     nsCOMPtr<nsIDOMXULMenuListElement> menulist =
         aFocusedElement->AsXULMenuList();
     if (!menulist) {
       nsCOMPtr<nsIDOMXULSelectControlElement> select =
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -180,10 +180,9 @@ ABSTRACT_FRAME_ID(nsIPopupContainer)
 ABSTRACT_FRAME_ID(nsIScrollableFrame)
 ABSTRACT_FRAME_ID(nsIScrollbarMediator)
 ABSTRACT_FRAME_ID(nsISelectControlFrame)
 ABSTRACT_FRAME_ID(nsISVGSVGFrame)
 ABSTRACT_FRAME_ID(nsIStatefulFrame)
 ABSTRACT_FRAME_ID(nsITableCellLayout)
 ABSTRACT_FRAME_ID(nsITableLayout)
 ABSTRACT_FRAME_ID(nsITextControlFrame)
-ABSTRACT_FRAME_ID(nsITreeBoxObject)
 ABSTRACT_FRAME_ID(nsSVGDisplayableFrame)
--- a/layout/xul/crashtests/365151.xul
+++ b/layout/xul/crashtests/365151.xul
@@ -4,17 +4,17 @@
         onload="boom()" class="reftest-wait">
 
 
 <script>
 function boom()
 {
  try {
   var tree = document.getElementById("tree");
-  var col = tree.treeBoxObject.columns.getFirstColumn();
+  var col = tree.columns.getFirstColumn();
   var treecols = document.getElementById("treecols");
   treecols.parentNode.removeChild(treecols);
   var x = col.x;
  } finally {
   document.documentElement.removeAttribute("class");
  }
 }
 </script>
--- a/layout/xul/nsXULTooltipListener.cpp
+++ b/layout/xul/nsXULTooltipListener.cpp
@@ -327,45 +327,44 @@ void nsXULTooltipListener::CheckTreeBody
   // get the boxObject of the documentElement of the document the tree is in
   nsCOMPtr<nsIBoxObject> bx;
   Document* doc = sourceNode->GetComposedDoc();
   if (doc) {
     ErrorResult ignored;
     bx = doc->GetBoxObjectFor(doc->GetRootElement(), ignored);
   }
 
-  nsCOMPtr<nsITreeBoxObject> obx;
-  GetSourceTreeBoxObject(getter_AddRefs(obx));
-  if (bx && obx) {
+  RefPtr<XULTreeElement> tree = GetSourceTree();
+  if (bx && tree) {
     int32_t x = aMouseEvent->ScreenX(CallerType::System);
     int32_t y = aMouseEvent->ScreenY(CallerType::System);
 
     int32_t row;
     RefPtr<nsTreeColumn> col;
     nsAutoString obj;
 
     // subtract off the documentElement's boxObject
     int32_t boxX, boxY;
     bx->GetScreenX(&boxX);
     bx->GetScreenY(&boxY);
     x -= boxX;
     y -= boxY;
 
-    obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj);
+    tree->GetCellAt(x, y, &row, getter_AddRefs(col), obj);
 
     // determine if we are going to need a titletip
     // XXX check the disabletitletips attribute on the tree content
     mNeedTitletip = false;
     int16_t colType = -1;
     if (col) {
       colType = col->Type();
     }
     if (row >= 0 && obj.EqualsLiteral("text") &&
         colType != TreeColumn_Binding::TYPE_PASSWORD) {
-      obx->IsCellCropped(row, col, &mNeedTitletip);
+      tree->IsCellCropped(row, col, &mNeedTitletip);
     }
 
     nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
     if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
       HideTooltip();
     }
 
     mLastTreeRow = row;
@@ -429,39 +428,20 @@ nsresult nsXULTooltipListener::ShowToolt
       mSourceNode = nullptr;
     }
   }
 
   return NS_OK;
 }
 
 #ifdef MOZ_XUL
-// XXX: "This stuff inside DEBUG_crap could be used to make tree tooltips work
-//       in the future."
-#ifdef DEBUG_crap
-static void GetTreeCellCoords(nsITreeBoxObject* aTreeBox,
-                              nsIContent* aSourceNode, int32_t aRow,
-                              nsTreeColumn* aCol, int32_t* aX, int32_t* aY) {
-  int32_t junk;
-  aTreeBox->GetCoordsForCellItem(aRow, aCol, EmptyCString(), aX, aY, &junk,
-                                 &junk);
-  RefPtr<nsXULElement> xulEl = nsXULElement::FromNode(aSourceNode);
-  nsCOMPtr<nsIBoxObject> bx = xulEl->GetBoxObject(IgnoreErrors());
-  int32_t myX, myY;
-  bx->GetX(&myX);
-  bx->GetY(&myY);
-  *aX += myX;
-  *aY += myY;
-}
-#endif
-
-static void SetTitletipLabel(nsITreeBoxObject* aTreeBox, Element* aTooltip,
+static void SetTitletipLabel(XULTreeElement* aTree, Element* aTooltip,
                              int32_t aRow, nsTreeColumn* aCol) {
   nsCOMPtr<nsITreeView> view;
-  aTreeBox->GetView(getter_AddRefs(view));
+  aTree->GetView(getter_AddRefs(view));
   if (view) {
     nsAutoString label;
 #ifdef DEBUG
     nsresult rv =
 #endif
         view->GetCellText(aRow, aCol, label);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get the cell text!");
     aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true);
@@ -470,20 +450,19 @@ static void SetTitletipLabel(nsITreeBoxO
 #endif
 
 void nsXULTooltipListener::LaunchTooltip() {
   nsCOMPtr<Element> currentTooltip = do_QueryReferent(mCurrentTooltip);
   if (!currentTooltip) return;
 
 #ifdef MOZ_XUL
   if (mIsSourceTree && mNeedTitletip) {
-    nsCOMPtr<nsITreeBoxObject> obx;
-    GetSourceTreeBoxObject(getter_AddRefs(obx));
+    RefPtr<XULTreeElement> tree = GetSourceTree();
 
-    SetTitletipLabel(obx, currentTooltip, mLastTreeRow, mLastTreeCol);
+    SetTitletipLabel(tree, currentTooltip, mLastTreeRow, mLastTreeCol);
     if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
       // Because of mutation events, currentTooltip can be null.
       return;
     }
     currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip,
                             NS_LITERAL_STRING("true"), true);
   } else {
     currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true);
@@ -692,29 +671,19 @@ void nsXULTooltipListener::KillTooltipTi
 }
 
 void nsXULTooltipListener::sTooltipCallback(nsITimer* aTimer, void* aListener) {
   RefPtr<nsXULTooltipListener> instance = sInstance;
   if (instance) instance->ShowTooltip();
 }
 
 #ifdef MOZ_XUL
-nsresult nsXULTooltipListener::GetSourceTreeBoxObject(
-    nsITreeBoxObject** aBoxObject) {
-  *aBoxObject = nullptr;
-
+XULTreeElement* nsXULTooltipListener::GetSourceTree() {
   nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
   if (mIsSourceTree && sourceNode) {
-    RefPtr<nsXULElement> xulEl =
-        nsXULElement::FromNodeOrNull(sourceNode->GetParent());
-    if (xulEl) {
-      nsCOMPtr<nsIBoxObject> bx = xulEl->GetBoxObject(IgnoreErrors());
-      nsCOMPtr<nsITreeBoxObject> obx(do_QueryInterface(bx));
-      if (obx) {
-        *aBoxObject = obx;
-        NS_ADDREF(*aBoxObject);
-        return NS_OK;
-      }
-    }
+    RefPtr<XULTreeElement> xulEl =
+        XULTreeElement::FromNodeOrNull(sourceNode->GetParent());
+    return xulEl;
   }
-  return NS_ERROR_FAILURE;
+
+  return nullptr;
 }
 #endif
--- a/layout/xul/nsXULTooltipListener.h
+++ b/layout/xul/nsXULTooltipListener.h
@@ -7,17 +7,17 @@
 #ifndef nsXULTooltipListener_h__
 #define nsXULTooltipListener_h__
 
 #include "nsIDOMEventListener.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #ifdef MOZ_XUL
-#include "nsITreeBoxObject.h"
+#include "XULTreeElement.h"
 #endif
 #include "nsIWeakReferenceUtils.h"
 #include "mozilla/Attributes.h"
 
 class nsIContent;
 class nsTreeColumn;
 
 namespace mozilla {
@@ -48,17 +48,17 @@ class nsXULTooltipListener final : publi
 
   // pref callback for when the "show tooltips" pref changes
   static bool sShowTooltips;
 
   void KillTooltipTimer();
 
 #ifdef MOZ_XUL
   void CheckTreeBodyMove(mozilla::dom::MouseEvent* aMouseEvent);
-  nsresult GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject);
+  mozilla::dom::XULTreeElement* GetSourceTree();
 #endif
 
   nsresult ShowTooltip();
   void LaunchTooltip();
   nsresult HideTooltip();
   nsresult DestroyTooltip();
   // This method tries to find a tooltip for aTarget.
   nsresult FindTooltip(nsIContent* aTarget, nsIContent** aTooltip);
--- a/layout/xul/tree/moz.build
+++ b/layout/xul/tree/moz.build
@@ -3,43 +3,37 @@
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'XUL')
 
 XPIDL_SOURCES += [
-    'nsITreeBoxObject.idl',
     'nsITreeSelection.idl',
     'nsITreeView.idl',
 ]
 
 XPIDL_MODULE = 'layout_xul_tree'
 
 EXPORTS += [
     'nsTreeColFrame.h',
     'nsTreeColumns.h',
     'nsTreeUtils.h',
 ]
 
-EXPORTS.mozilla.dom += [
-    'TreeBoxObject.h'
-]
-
 UNIFIED_SOURCES += [
     'nsTreeBodyFrame.cpp',
     'nsTreeColFrame.cpp',
     'nsTreeColumns.cpp',
     'nsTreeContentView.cpp',
     'nsTreeImageListener.cpp',
     'nsTreeSelection.cpp',
     'nsTreeStyleCache.cpp',
     'nsTreeUtils.cpp',
-    'TreeBoxObject.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '..',
     '../../base',
     '../../forms',
     '../../generic',
deleted file mode 100644
--- a/layout/xul/tree/nsITreeBoxObject.idl
+++ /dev/null
@@ -1,123 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsITreeView;
-
-webidl Element;
-webidl TreeColumn;
-webidl TreeColumns;
-
-/**
- * This interface cannot become builtinclass until bug 1438525 is fixed.
- */
-[scriptable, uuid(f3da0c5e-51f5-45f0-b2cd-6be3ab6847ae)]
-interface nsITreeBoxObject : nsISupports
-{
-  /**
-   * Obtain the columns.
-   */
-  readonly attribute TreeColumns columns;
-
-  /**
-   * The view that backs the tree and that supplies it with its data.
-   * It is dynamically settable, either using a view attribute on the
-   * tree tag or by setting this attribute to a new value.
-   */
-  attribute nsITreeView view;
-
-  /**
-   * Whether or not we are currently focused.
-   */
-  attribute boolean focused;
-
-  /**
-   * Obtain the treebody content node
-   */
-  readonly attribute Element treeBody;
-
-  /**
-   * Obtain the height of a row.
-   */
-  readonly attribute long rowHeight;
-
-  /**
-   * Obtain the width of a row.
-   */
-  readonly attribute long rowWidth;
-
-  /**
-   * Get the index of the first visible row.
-   */
-  long getFirstVisibleRow();
-
-  /**
-   * Get the index of the last visible row.
-   */
-  long getLastVisibleRow();
-
-  /**
-   * Ensures that a row at a given index is visible.
-   */
-  void ensureRowIsVisible(in long index);
-
-  /**
-   * Invalidation methods for fine-grained painting control.
-   */
-  void invalidate();
-  void invalidateColumn(in TreeColumn col);
-  void invalidateRow(in long index);
-  void invalidateCell(in long row, in TreeColumn col);
-  void invalidateRange(in long startIndex, in long endIndex);
-
-  /**
-   * A hit test that can tell you what cell the mouse is over.  Row is the row index
-   * hit,  returns -1 for invalid mouse coordinates.  ColID is the column hit.
-   * ChildElt is the pseudoelement hit: this can have values of
-   * "cell", "twisty", "image", and "text".
-   *
-   * The coordinate system is the client coordinate system for the
-   * document this boxObject lives in, and the units are CSS pixels.
-   */
-  void getCellAt(in long x, in long y, out long row, out TreeColumn col, out AString childElt);
-
-  /** 
-   * Find the coordinates of an element within a specific cell. 
-   */
-  void getCoordsForCellItem(in long row, in TreeColumn col, in AString element,
-                            out long x, out long y, out long width, out long height);
-
-  /** 
-   * Determine if the text of a cell is being cropped or not.
-   */
-  boolean isCellCropped(in long row, in TreeColumn col);
-
-  /**
-   * The view is responsible for calling these notification methods when
-   * rows are added or removed.  Index is the position at which the new
-   * rows were added or at which rows were removed.  For
-   * non-contiguous additions/removals, this method should be called multiple times.
-   */
-  void rowCountChanged(in long index, in long count);
-  
-  /**
-   * Notify the tree that the view is about to perform a batch
-   * update, that is, add, remove or invalidate several rows at once.
-   * This must be followed by calling endUpdateBatch(), otherwise the tree
-   * will get out of sync.
-   */
-  void beginUpdateBatch();
-
-  /**
-   * Notify the tree that the view has completed a batch update.
-   */
-  void endUpdateBatch();
-
-  /**
-   * Called on a theme switch to flush out the tree's style and image caches.
-   */
-  void clearStyleAndImageCaches();
-};
--- a/layout/xul/tree/nsITreeSelection.idl
+++ b/layout/xul/tree/nsITreeSelection.idl
@@ -1,24 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-interface nsITreeBoxObject;
+#include "nsISupports.idl"
 
-#include "nsISupports.idl"
+webidl XULTreeElement;
 
 [scriptable, uuid(ab6fe746-300b-4ab4-abb9-1c0e3977874c)]
 interface nsITreeSelection : nsISupports
 {
   /**
    * The tree widget for this selection.
    */
-  attribute nsITreeBoxObject tree;
+  attribute XULTreeElement tree;
 
   /**
    * This attribute is a boolean indicating single selection.
    */
   readonly attribute boolean single;
 
   /**
    * The number of rows currently selected in this tree.
--- a/layout/xul/tree/nsITreeView.idl
+++ b/layout/xul/tree/nsITreeView.idl
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsITreeBoxObject;
 interface nsITreeSelection;
 
 webidl DataTransfer;
 webidl TreeColumn;
+webidl XULTreeElement;
 
 [scriptable, uuid(091116f0-0bdc-4b32-b9c8-c8d5a37cb088)]
 interface nsITreeView : nsISupports
 {
   /**
    * The total number of rows in the tree (including the offscreen rows).
    */
   readonly attribute long rowCount;
@@ -125,17 +125,17 @@ interface nsITreeView : nsISupports
    * The text for a given cell.  If a column consists only of an image, then
    * the empty string is returned.
    */
   AString getCellText(in long row, in TreeColumn col);
 
   /**
    * Called during initialization to link the view to the front end box object.
    */
-  void setTree(in nsITreeBoxObject tree);
+  void setTree(in XULTreeElement tree);
 
   /**
    * Called on the view when an item is opened or closed.
    */
   void toggleOpenState(in long index);
 
   /**
    * Called on the view when a header is clicked.
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -28,25 +28,25 @@
 #include "nsTreeImageListener.h"
 
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 
 #include "gfxContext.h"
 #include "nsIContent.h"
 #include "mozilla/ComputedStyle.h"
-#include "nsIBoxObject.h"
 #include "mozilla/dom/Document.h"
 #include "nsCSSRendering.h"
 #include "nsString.h"
 #include "nsContainerFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsVariant.h"
 #include "nsWidgetsCID.h"
+#include "nsIFrameInlines.h"
 #include "nsBoxFrame.h"
 #include "nsIURL.h"
 #include "nsBoxLayoutState.h"
 #include "nsTreeContentView.h"
 #include "nsTreeUtils.h"
 #include "nsStyleConsts.h"
 #include "nsITheme.h"
 #include "imgIRequest.h"
@@ -56,17 +56,16 @@
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsDisplayList.h"
 #include "mozilla/dom/CustomEvent.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
-#include "mozilla/dom/TreeBoxObject.h"
 #include "mozilla/dom/TreeColumnBinding.h"
 #include <algorithm>
 #include "ScrollbarActivity.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #include "nsIWritablePropertyBag2.h"
 #endif
@@ -154,36 +153,37 @@ static void AdjustForBorderPadding(Compu
 
 void nsTreeBodyFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                            nsIFrame* aPrevInFlow) {
   nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   mIndentation = GetIndentation();
   mRowHeight = GetRowHeight();
 
-  EnsureBoxObject();
+  // Call GetBaseElement so that mTree is assigned.
+  GetBaseElement();
 
   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
     mScrollbarActivity =
         new ScrollbarActivity(static_cast<nsIScrollbarMediator*>(this));
   }
 }
 
 nsSize nsTreeBodyFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) {
   EnsureView();
 
-  Element* baseElement = GetBaseElement();
+  RefPtr<XULTreeElement> tree(GetBaseElement());
 
   nsSize min(0, 0);
   int32_t desiredRows;
-  if (MOZ_UNLIKELY(!baseElement)) {
+  if (MOZ_UNLIKELY(!tree)) {
     desiredRows = 0;
   } else {
     nsAutoString rows;
-    baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
+    tree->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
     if (!rows.IsEmpty()) {
       nsresult err;
       desiredRows = rows.ToInteger(&err);
       mPageLength = desiredRows;
     } else {
       desiredRows = 0;
     }
   }
@@ -243,103 +243,58 @@ void nsTreeBodyFrame::DestroyFrom(nsIFra
   // Make sure we cancel any posted callbacks.
   if (mReflowCallbackPosted) {
     PresShell()->CancelReflowCallback(this);
     mReflowCallbackPosted = false;
   }
 
   if (mColumns) mColumns->SetTree(nullptr);
 
-  // Save off our info into the box object.
-  nsCOMPtr<nsPIBoxObject> box(do_QueryInterface(mTreeBoxObject));
-  if (box) {
-    if (mTopRowIndex > 0) {
-      nsAutoString topRowStr;
-      topRowStr.AssignLiteral("topRow");
-      nsAutoString topRow;
-      topRow.AppendInt(mTopRowIndex);
-      box->SetProperty(topRowStr.get(), topRow.get());
-    }
-
-    // Always null out the cached tree body frame.
-    box->ClearCachedValues();
-
-    mTreeBoxObject = nullptr;  // Drop our ref here.
+  if (mTree) {
+    mTree->BodyDestroyed(mTopRowIndex);
   }
 
   if (mView) {
     nsCOMPtr<nsITreeSelection> sel;
     mView->GetSelection(getter_AddRefs(sel));
     if (sel) sel->SetTree(nullptr);
     mView->SetTree(nullptr);
     mView = nullptr;
   }
 
   nsLeafBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
 }
 
-void nsTreeBodyFrame::EnsureBoxObject() {
-  if (!mTreeBoxObject) {
-    nsIContent* parent = GetBaseElement();
-    if (parent) {
-      Document* nsDoc = parent->GetComposedDoc();
-      if (!nsDoc)  // there may be no document, if we're called from Destroy()
-        return;
-      ErrorResult ignored;
-      nsCOMPtr<nsIBoxObject> box =
-          nsDoc->GetBoxObjectFor(parent->AsElement(), ignored);
-      // Ensure that we got a native box object.
-      nsCOMPtr<nsPIBoxObject> pBox = do_QueryInterface(box);
-      if (pBox) {
-        nsCOMPtr<nsITreeBoxObject> realTreeBoxObject = do_QueryInterface(pBox);
-        if (realTreeBoxObject) {
-          nsTreeBodyFrame* innerTreeBoxObject =
-              static_cast<dom::TreeBoxObject*>(realTreeBoxObject.get())
-                  ->GetCachedTreeBodyFrame();
-          NS_ENSURE_TRUE_VOID(!innerTreeBoxObject ||
-                              innerTreeBoxObject == this);
-          mTreeBoxObject = realTreeBoxObject;
-        }
-      }
-    }
-  }
-}
-
 void nsTreeBodyFrame::EnsureView() {
   if (!mView) {
     if (PresShell()->IsReflowLocked()) {
       if (!mReflowCallbackPosted) {
         mReflowCallbackPosted = true;
         PresShell()->PostReflowCallback(this);
       }
       return;
     }
-    nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
-    if (box) {
-      AutoWeakFrame weakFrame(this);
+
+    AutoWeakFrame weakFrame(this);
+
+    RefPtr<XULTreeElement> tree = GetBaseElement();
+    if (tree) {
       nsCOMPtr<nsITreeView> treeView;
-      mTreeBoxObject->GetView(getter_AddRefs(treeView));
+      tree->GetView(getter_AddRefs(treeView));
       if (treeView && weakFrame.IsAlive()) {
-        nsString rowStr;
-        box->GetProperty(u"topRow", getter_Copies(rowStr));
-        nsresult error;
-        int32_t rowIndex = rowStr.ToInteger(&error);
+        int32_t rowIndex = tree->GetCachedTopVisibleRow();
 
         // Set our view.
         SetView(treeView);
         NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
 
         // Scroll to the given row.
         // XXX is this optimal if we haven't laid out yet?
         ScrollToRow(rowIndex);
         NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
-
-        // Clear out the property info for the top row, but we always keep the
-        // view current.
-        box->RemoveProperty(u"topRow");
       }
     }
   }
 }
 
 void nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect,
                                            nscoord aHorzWidth) {
   if (!mReflowCallbackPosted &&
@@ -379,17 +334,17 @@ bool nsTreeBodyFrame::ReflowFinished() {
       mPageLength =
           (mRowHeight > 0) ? (mInnerBox.height / mRowHeight) : mRowCount;
     }
 
     int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
     if (mTopRowIndex > lastPageTopRow)
       ScrollToRowInternal(parts, lastPageTopRow);
 
-    Element* treeContent = GetBaseElement();
+    XULTreeElement* treeContent = GetBaseElement();
     if (treeContent && treeContent->AttrValueIs(
                            kNameSpaceID_None, nsGkAtoms::keepcurrentinview,
                            nsGkAtoms::_true, eCaseMatters)) {
       // make sure that the current selected item is still
       // visible after the tree changes size.
       nsCOMPtr<nsITreeSelection> sel;
       mView->GetSelection(getter_AddRefs(sel));
       if (sel) {
@@ -436,42 +391,42 @@ nsresult nsTreeBodyFrame::SetView(nsITre
 
   // Tree, meet the view.
   mView = aView;
 
   // Changing the view causes us to refetch our data.  This will
   // necessarily entail a full invalidation of the tree.
   Invalidate();
 
-  nsIContent* treeContent = GetBaseElement();
+  RefPtr<XULTreeElement> treeContent = GetBaseElement();
   if (treeContent) {
 #ifdef ACCESSIBILITY
     nsAccessibilityService* accService = nsIPresShell::AccService();
     if (accService)
       accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent,
                                   mView);
 #endif
     FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent);
   }
 
   if (mView) {
     // Give the view a new empty selection object to play with, but only if it
     // doesn't have one already.
     nsCOMPtr<nsITreeSelection> sel;
     mView->GetSelection(getter_AddRefs(sel));
     if (sel) {
-      sel->SetTree(mTreeBoxObject);
+      sel->SetTree(treeContent);
     } else {
-      NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
+      NS_NewTreeSelection(treeContent, getter_AddRefs(sel));
       mView->SetSelection(sel);
     }
 
     // View, meet the tree.
     AutoWeakFrame weakFrame(this);
-    mView->SetTree(mTreeBoxObject);
+    mView->SetTree(treeContent);
     NS_ENSURE_STATE(weakFrame.IsAlive());
     mView->GetRowCount(&mRowCount);
 
     if (!PresShell()->IsReflowLocked()) {
       // The scrollbar will need to be updated.
       FullScrollbarsUpdate(false);
     } else if (!mReflowCallbackPosted) {
       mReflowCallbackPosted = true;
@@ -681,18 +636,18 @@ static void FindScrollParts(nsIFrame* aC
           !aResult->mColumnsScrollFrame)) {
     FindScrollParts(child, aResult);
     child = child->GetNextSibling();
   }
 }
 
 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts() {
   ScrollParts result = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-  nsIContent* baseElement = GetBaseElement();
-  nsIFrame* treeFrame = baseElement ? baseElement->GetPrimaryFrame() : nullptr;
+  XULTreeElement* tree = GetBaseElement();
+  nsIFrame* treeFrame = tree ? tree->GetPrimaryFrame() : nullptr;
   if (treeFrame) {
     // The way we do this, searching through the entire frame subtree, is pretty
     // dumb! We should know where these frames are.
     FindScrollParts(treeFrame, &result);
     if (result.mHScrollbar) {
       result.mHScrollbar->SetScrollbarMediatorContent(GetContent());
       nsIFrame* f = do_QueryFrame(result.mHScrollbar);
       result.mHScrollbarContent = f->GetContent()->AsElement();
@@ -1781,20 +1736,20 @@ void nsTreeBodyFrame::PrefillPropertyArr
     }
 
     // odd or even
     if (aRowIndex % 2)
       mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::odd);
     else
       mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::even);
 
-    Element* baseContent = GetBaseElement();
-    if (baseContent &&
-        baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing))
+    XULTreeElement* tree = GetBaseElement();
+    if (tree && tree->HasAttr(kNameSpaceID_None, nsGkAtoms::editing)) {
       mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::editing);
+    }
 
     // multiple columns
     if (mColumns->GetColumnAt(1))
       mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::multicol);
   }
 
   if (aCol) {
     mScratchArray.AppendElement(aCol->GetAtom());
@@ -2572,18 +2527,18 @@ void nsTreeBodyFrame::BuildDisplayList(n
   // Bail out now if there's no view or we can't run script because the
   // document is a zombie
   if (!mView || !GetContent()->GetComposedDoc()->GetWindow()) return;
 
   nsDisplayItem* item = MakeDisplayItem<nsDisplayTreeBody>(aBuilder, this);
   aLists.Content()->AppendToTop(item);
 
 #ifdef XP_MACOSX
-  nsIContent* baseElement = GetBaseElement();
-  nsIFrame* treeFrame = baseElement ? baseElement->GetPrimaryFrame() : nullptr;
+  XULTreeElement* tree = GetBaseElement();
+  nsIFrame* treeFrame = tree ? tree->GetPrimaryFrame() : nullptr;
   nsCOMPtr<nsITreeSelection> selection;
   mView->GetSelection(getter_AddRefs(selection));
   nsITheme* theme = PresContext()->GetTheme();
   // On Mac, we support native theming of selected rows. On 10.10 and higher,
   // this means applying vibrancy which require us to register the theme
   // geometrics for the row. In order to make the vibrancy effect to work
   // properly, we also need the tree to be themed as a source list.
   if (selection && treeFrame && theme &&
@@ -3955,28 +3910,31 @@ void nsTreeBodyFrame::ThumbMoved(nsScrol
 
 // The style cache.
 ComputedStyle* nsTreeBodyFrame::GetPseudoComputedStyle(
     nsCSSAnonBoxPseudoStaticAtom* aPseudoElement) {
   return mStyleCache.GetComputedStyle(PresContext(), mContent, mComputedStyle,
                                       aPseudoElement, mScratchArray);
 }
 
-Element* nsTreeBodyFrame::GetBaseElement() {
-  nsIFrame* parent = GetParent();
-  while (parent) {
-    nsIContent* content = parent->GetContent();
-    if (content && content->IsXULElement(nsGkAtoms::tree)) {
-      return content->AsElement();
+XULTreeElement* nsTreeBodyFrame::GetBaseElement() {
+  if (!mTree) {
+    nsIFrame* parent = GetParent();
+    while (parent) {
+      nsIContent* content = parent->GetContent();
+      if (content && content->IsXULElement(nsGkAtoms::tree)) {
+        mTree = XULTreeElement::FromNodeOrNull(content->AsElement());
+        break;
+      }
+
+      parent = parent->GetInFlowParent();
     }
-
-    parent = parent->GetParent();
   }
 
-  return nullptr;
+  return mTree;
 }
 
 nsresult nsTreeBodyFrame::ClearStyleAndImageCaches() {
   mStyleCache.Clear();
   CancelImageRequests();
   mImageCache.Clear();
   return NS_OK;
 }
@@ -4235,20 +4193,20 @@ static void InitCustomEvent(CustomEvent*
     return;
   }
 
   aEvent->InitCustomEvent(cx, aType, /* aCanBubble = */ true,
                           /* aCancelable = */ false, detail);
 }
 
 void nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount) {
-  nsCOMPtr<nsIContent> content(GetBaseElement());
-  if (!content) return;
-
-  nsCOMPtr<Document> doc = content->OwnerDoc();
+  RefPtr<XULTreeElement> tree(GetBaseElement());
+  if (!tree) return;
+
+  RefPtr<Document> doc = tree->OwnerDoc();
   MOZ_ASSERT(doc);
 
   RefPtr<Event> event = doc->CreateEvent(NS_LITERAL_STRING("customevent"),
                                          CallerType::System, IgnoreErrors());
 
   CustomEvent* treeEvent = event->AsCustomEvent();
   if (!treeEvent) {
     return;
@@ -4266,28 +4224,28 @@ void nsTreeBodyFrame::FireRowCountChange
   // Set 'count' data - the number of changed rows.
   propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount);
 
   InitCustomEvent(treeEvent, NS_LITERAL_STRING("TreeRowCountChanged"), propBag);
 
   event->SetTrusted(true);
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
-      new AsyncEventDispatcher(content, event);
+      new AsyncEventDispatcher(tree, event);
   asyncDispatcher->PostDOMEvent();
 }
 
 void nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx,
                                           int32_t aEndRowIdx,
                                           nsTreeColumn* aStartCol,
                                           nsTreeColumn* aEndCol) {
-  nsCOMPtr<nsIContent> content(GetBaseElement());
-  if (!content) return;
-
-  nsCOMPtr<Document> doc = content->OwnerDoc();
+  RefPtr<XULTreeElement> tree(GetBaseElement());
+  if (!tree) return;
+
+  RefPtr<Document> doc = tree->OwnerDoc();
 
   RefPtr<Event> event = doc->CreateEvent(NS_LITERAL_STRING("customevent"),
                                          CallerType::System, IgnoreErrors());
 
   CustomEvent* treeEvent = event->AsCustomEvent();
   if (!treeEvent) {
     return;
   }
@@ -4317,17 +4275,17 @@ void nsTreeBodyFrame::FireInvalidateEven
     propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"), endColIdx);
   }
 
   InitCustomEvent(treeEvent, NS_LITERAL_STRING("TreeInvalidated"), propBag);
 
   event->SetTrusted(true);
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
-      new AsyncEventDispatcher(content, event);
+      new AsyncEventDispatcher(tree, event);
   asyncDispatcher->PostDOMEvent();
 }
 #endif
 
 class nsOverflowChecker : public Runnable {
  public:
   explicit nsOverflowChecker(nsTreeBodyFrame* aFrame)
       : mozilla::Runnable("nsOverflowChecker"), mFrame(aFrame) {}
--- a/layout/xul/tree/nsTreeBodyFrame.h
+++ b/layout/xul/tree/nsTreeBodyFrame.h
@@ -179,20 +179,18 @@ class nsTreeBodyFrame final : public nsL
     nsIFrame* mColumnsFrame;
     nsIScrollableFrame* mColumnsScrollFrame;
   };
 
   ImgDrawResult PaintTreeBody(gfxContext& aRenderingContext,
                               const nsRect& aDirtyRect, nsPoint aPt,
                               nsDisplayListBuilder* aBuilder);
 
-  nsITreeBoxObject* GetTreeBoxObject() const { return mTreeBoxObject; }
-
   // Get the base element, <tree>
-  mozilla::dom::Element* GetBaseElement();
+  mozilla::dom::XULTreeElement* GetBaseElement();
 
   bool GetVerticalOverflow() const { return mVerticalOverflow; }
   bool GetHorizontalOverflow() const { return mHorizontalOverflow; }
 
   // This returns the property array where atoms are stored for style during
   // draw, whether the row currently being drawn is selected, hovered, etc.
   const mozilla::AtomArray& GetPropertyArrayForCurrentDrawingItem() {
     return mScratchArray;
@@ -366,19 +364,16 @@ class nsTreeBodyFrame final : public nsL
   nsresult ScrollInternal(const ScrollParts& aParts, int32_t aRow);
   nsresult ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow);
   nsresult ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition);
   nsresult EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow);
 
   // Convert client pixels into appunits in our coordinate space.
   nsPoint AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY);
 
-  // Cache the box object
-  void EnsureBoxObject();
-
   void EnsureView();
 
   nsresult GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
                         gfxContext* aRenderingContext, nscoord& aDesiredSize,
                         nscoord& aCurrentSize);
   nscoord CalcMaxRowWidth();
 
   // Translate the given rect horizontally from tree coordinates into the
@@ -525,18 +520,18 @@ class nsTreeBodyFrame final : public nsL
   };
 
   Slots* mSlots;
 
   nsRevocableEventPtr<ScrollEvent> mScrollEvent;
 
   RefPtr<ScrollbarActivity> mScrollbarActivity;
 
-  // The cached box object parent.
-  nsCOMPtr<nsITreeBoxObject> mTreeBoxObject;
+  // The <tree> element containing this treebody.
+  RefPtr<mozilla::dom::XULTreeElement> mTree;
 
   // Cached column information.
   RefPtr<nsTreeColumns> mColumns;
 
   // The current view for this tree widget.  We get all of our row and cell data
   // from the view.
   nsCOMPtr<nsITreeView> mView;
 
--- a/layout/xul/tree/nsTreeColFrame.cpp
+++ b/layout/xul/tree/nsTreeColFrame.cpp
@@ -5,19 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCOMPtr.h"
 #include "nsTreeColFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsNameSpaceManager.h"
-#include "nsIBoxObject.h"
 #include "mozilla/ErrorResult.h"
-#include "mozilla/dom/TreeBoxObject.h"
 #include "nsTreeColumns.h"
 #include "nsDisplayList.h"
 #include "nsTreeBodyFrame.h"
 #include "nsXULElement.h"
 
 using namespace mozilla;
 
 //
@@ -134,53 +132,37 @@ nsresult nsTreeColFrame::AttributeChange
 
 void nsTreeColFrame::SetXULBounds(nsBoxLayoutState& aBoxLayoutState,
                                   const nsRect& aRect,
                                   bool aRemoveOverflowArea) {
   nscoord oldWidth = mRect.width;
 
   nsBoxFrame::SetXULBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
   if (mRect.width != oldWidth) {
-    nsITreeBoxObject* treeBoxObject = GetTreeBoxObject();
-    if (treeBoxObject) {
-      treeBoxObject->Invalidate();
+    RefPtr<XULTreeElement> tree = GetTree();
+    if (tree) {
+      tree->Invalidate();
     }
   }
 }
 
-nsITreeBoxObject* nsTreeColFrame::GetTreeBoxObject() {
-  nsITreeBoxObject* result = nullptr;
-
+XULTreeElement* nsTreeColFrame::GetTree() {
   nsIContent* parent = mContent->GetParent();
-  if (parent) {
-    nsIContent* grandParent = parent->GetParent();
-    RefPtr<nsXULElement> treeElement =
-        nsXULElement::FromNodeOrNull(grandParent);
-    if (treeElement) {
-      nsCOMPtr<nsIBoxObject> boxObject =
-          treeElement->GetBoxObject(IgnoreErrors());
-
-      nsCOMPtr<nsITreeBoxObject> treeBoxObject = do_QueryInterface(boxObject);
-      result = treeBoxObject.get();
-    }
-  }
-  return result;
+  return parent ? XULTreeElement::FromNodeOrNull(parent->GetParent()) : nullptr;
 }
 
 void nsTreeColFrame::InvalidateColumns(bool aCanWalkFrameTree) {
-  nsITreeBoxObject* treeBoxObject = GetTreeBoxObject();
-  if (treeBoxObject) {
+  RefPtr<XULTreeElement> tree = GetTree();
+  if (tree) {
     RefPtr<nsTreeColumns> columns;
 
     if (aCanWalkFrameTree) {
-      treeBoxObject->GetColumns(getter_AddRefs(columns));
+      tree->GetColumns(getter_AddRefs(columns));
     } else {
-      nsTreeBodyFrame* body =
-          static_cast<mozilla::dom::TreeBoxObject*>(treeBoxObject)
-              ->GetCachedTreeBodyFrame();
+      nsTreeBodyFrame* body = tree->GetCachedTreeBodyFrame();
       if (body) {
         columns = body->Columns();
       }
     }
 
     if (columns) columns->InvalidateColumns();
   }
 }
--- a/layout/xul/tree/nsTreeColFrame.h
+++ b/layout/xul/tree/nsTreeColFrame.h
@@ -3,17 +3,21 @@
 /* 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/. */
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsBoxFrame.h"
 
-class nsITreeBoxObject;
+namespace mozilla {
+namespace dom {
+class XULTreeElement;
+}
+}  // namespace mozilla
 
 nsIFrame* NS_NewTreeColFrame(nsIPresShell* aPresShell,
                              mozilla::ComputedStyle* aStyle);
 
 class nsTreeColFrame final : public nsBoxFrame {
  public:
   NS_DECL_FRAMEARENA_HELPERS(nsTreeColFrame)
 
@@ -38,18 +42,18 @@ class nsTreeColFrame final : public nsBo
 
   friend nsIFrame* NS_NewTreeColFrame(nsIPresShell* aPresShell,
                                       ComputedStyle* aStyle);
 
  protected:
   virtual ~nsTreeColFrame();
 
   /**
-   * @return the tree box object of the tree this column belongs to, or nullptr.
+   * @return the tree that this column belongs to, or nullptr.
    */
-  nsITreeBoxObject* GetTreeBoxObject();
+  mozilla::dom::XULTreeElement* GetTree();
 
   /**
    * Helper method that gets the TreeColumns object this column belongs to
    * and calls InvalidateColumns() on it.
    */
   void InvalidateColumns(bool aCanWalkFrameTree = true);
 };
--- a/layout/xul/tree/nsTreeColumns.cpp
+++ b/layout/xul/tree/nsTreeColumns.cpp
@@ -8,17 +8,16 @@
 #include "nsGkAtoms.h"
 #include "nsIBoxObject.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsContentUtils.h"
 #include "nsTreeBodyFrame.h"
 #include "mozilla/dom/Element.h"
-#include "mozilla/dom/TreeBoxObject.h"
 #include "mozilla/dom/TreeColumnBinding.h"
 #include "mozilla/dom/TreeColumnsBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Column class that caches all the info about our column.
 nsTreeColumn::nsTreeColumn(nsTreeColumns* aColumns, dom::Element* aElement)
@@ -255,20 +254,22 @@ nsIContent* nsTreeColumns::GetParentObje
   return mTree ? mTree->GetBaseElement() : nullptr;
 }
 
 /* virtual */ JSObject* nsTreeColumns::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return dom::TreeColumns_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-dom::TreeBoxObject* nsTreeColumns::GetTree() const {
-  return mTree ? static_cast<mozilla::dom::TreeBoxObject*>(
-                     mTree->GetTreeBoxObject())
-               : nullptr;
+XULTreeElement* nsTreeColumns::GetTree() const {
+  if (!mTree) {
+    return nullptr;
+  }
+
+  return XULTreeElement::FromNodeOrNull(mTree->GetBaseElement());
 }
 
 uint32_t nsTreeColumns::Count() {
   EnsureColumns();
   uint32_t count = 0;
   for (nsTreeColumn* currCol = mFirstColumn; currCol;
        currCol = currCol->GetNext()) {
     ++count;
@@ -439,16 +440,18 @@ nsTreeColumn* nsTreeColumns::GetPrimaryC
     }
   }
   return nullptr;
 }
 
 void nsTreeColumns::EnsureColumns() {
   if (mTree && !mFirstColumn) {
     nsIContent* treeContent = mTree->GetBaseElement();
+    if (!treeContent) return;
+
     nsIContent* colsContent =
         nsTreeUtils::GetDescendantChild(treeContent, nsGkAtoms::treecols);
     if (!colsContent) return;
 
     nsIContent* colContent =
         nsTreeUtils::GetDescendantChild(colsContent, nsGkAtoms::treecol);
     if (!colContent) return;
 
--- a/layout/xul/tree/nsTreeColumns.h
+++ b/layout/xul/tree/nsTreeColumns.h
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTreeColumns_h__
 #define nsTreeColumns_h__
 
-#include "nsITreeBoxObject.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCoord.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsQueryObject.h"
 #include "nsWrapperCache.h"
 #include "nsString.h"
 
@@ -22,17 +21,17 @@ class nsTreeColumns;
 class nsIFrame;
 class nsIContent;
 struct nsRect;
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 class Element;
-class TreeBoxObject;
+class XULTreeElement;
 }  // namespace dom
 }  // namespace mozilla
 
 #define NS_TREECOLUMN_IMPL_CID                       \
   { /* 02cd1963-4b5d-4a6c-9223-814d3ade93a3 */       \
     0x02cd1963, 0x4b5d, 0x4a6c, {                    \
       0x92, 0x23, 0x81, 0x4d, 0x3a, 0xde, 0x93, 0xa3 \
     }                                                \
@@ -157,17 +156,17 @@ class nsTreeColumns final : public nsISu
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsTreeColumns)
 
   nsIContent* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
-  mozilla::dom::TreeBoxObject* GetTree() const;
+  mozilla::dom::XULTreeElement* GetTree() const;
   uint32_t Count();
   uint32_t Length() { return Count(); }
 
   nsTreeColumn* GetFirstColumn() {
     EnsureColumns();
     return mFirstColumn;
   }
   nsTreeColumn* GetLastColumn();
--- a/layout/xul/tree/nsTreeContentView.cpp
+++ b/layout/xul/tree/nsTreeContentView.cpp
@@ -1,27 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNameSpaceManager.h"
 #include "nsGkAtoms.h"
-#include "nsIBoxObject.h"
 #include "nsTreeUtils.h"
 #include "nsTreeContentView.h"
 #include "ChildIterator.h"
 #include "nsError.h"
 #include "nsXULSortService.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Element.h"
-#include "mozilla/dom/TreeBoxObject.h"
 #include "mozilla/dom/TreeContentViewBinding.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/Document.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // A content model view implementation for the tree.
@@ -77,35 +75,32 @@ class Row {
 
 // We don't reference count the reference to the document
 // If the document goes away first, we'll be informed and we
 // can drop our reference.
 // If we go away first, we'll get rid of ourselves from the
 // document's observer list.
 
 nsTreeContentView::nsTreeContentView(void)
-    : mBoxObject(nullptr),
-      mSelection(nullptr),
-      mRoot(nullptr),
-      mDocument(nullptr) {}
+    : mTree(nullptr), mSelection(nullptr), mDocument(nullptr) {}
 
 nsTreeContentView::~nsTreeContentView(void) {
   // Remove ourselves from mDocument's observers.
   if (mDocument) mDocument->RemoveObserver(this);
 }
 
 nsresult NS_NewTreeContentView(nsITreeView** aResult) {
   *aResult = new nsTreeContentView;
   if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
   NS_ADDREF(*aResult);
   return NS_OK;
 }
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsTreeContentView, mBoxObject, mSelection,
-                                      mRoot, mBody)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsTreeContentView, mTree, mSelection,
+                                      mBody)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView)
   NS_INTERFACE_MAP_ENTRY(nsITreeView)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
@@ -113,17 +108,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 NS_INTERFACE_MAP_END
 
 JSObject* nsTreeContentView::WrapObject(JSContext* aCx,
                                         JS::Handle<JSObject*> aGivenProto) {
   return TreeContentView_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-nsISupports* nsTreeContentView::GetParentObject() { return mBoxObject; }
+nsISupports* nsTreeContentView::GetParentObject() { return mTree; }
 
 NS_IMETHODIMP
 nsTreeContentView::GetRowCount(int32_t* aRowCount) {
   *aRowCount = mRows.Length();
 
   return NS_OK;
 }
 
@@ -500,53 +495,36 @@ nsTreeContentView::GetCellText(int32_t a
                                nsAString& _retval) {
   NS_ENSURE_ARG(aCol);
 
   ErrorResult rv;
   GetCellText(aRow, *aCol, _retval, rv);
   return rv.StealNSResult();
 }
 
-void nsTreeContentView::SetTree(TreeBoxObject* aTree, ErrorResult& aError) {
+void nsTreeContentView::SetTree(XULTreeElement* aTree, ErrorResult& aError) {
   aError = SetTree(aTree);
 }
 
 NS_IMETHODIMP
-nsTreeContentView::SetTree(nsITreeBoxObject* aTree) {
+nsTreeContentView::SetTree(XULTreeElement* aTree) {
   ClearRows();
 
-  mBoxObject = aTree;
-
-  MOZ_ASSERT(!mRoot, "mRoot should have been cleared out by ClearRows");
+  mTree = aTree;
 
   if (aTree) {
-    // Get our root element
-    nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
-    if (!boxObject) {
-      mBoxObject = nullptr;
-      return NS_ERROR_INVALID_ARG;
-    }
-
-    {  // Scope for element
-      RefPtr<dom::Element> element;
-      boxObject->GetElement(getter_AddRefs(element));
-
-      mRoot = element.forget();
-      NS_ENSURE_STATE(mRoot);
-    }
-
     // Add ourselves to document's observers.
-    Document* document = mRoot->GetComposedDoc();
+    Document* document = mTree->GetComposedDoc();
     if (document) {
       document->AddObserver(this);
       mDocument = document;
     }
 
     RefPtr<dom::Element> bodyElement;
-    mBoxObject->GetTreeBody(getter_AddRefs(bodyElement));
+    mTree->GetTreeBody(getter_AddRefs(bodyElement));
     if (bodyElement) {
       mBody = bodyElement.forget();
       int32_t index = 0;
       Serialize(mBody, -1, &index, mRows);
     }
   }
 
   return NS_OK;
@@ -574,17 +552,17 @@ NS_IMETHODIMP
 nsTreeContentView::ToggleOpenState(int32_t aIndex) {
   ErrorResult rv;
   ToggleOpenState(aIndex, rv);
   return rv.StealNSResult();
 }
 
 void nsTreeContentView::CycleHeader(nsTreeColumn& aColumn,
                                     ErrorResult& aError) {
-  if (!mRoot) return;
+  if (!mTree) return;
 
   RefPtr<Element> column = aColumn.Element();
   nsAutoString sort;
   column->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
   if (!sort.IsEmpty()) {
     nsAutoString sortdirection;
     static Element::AttrValuesArray strings[] = {
         nsGkAtoms::ascending, nsGkAtoms::descending, nullptr};
@@ -601,17 +579,17 @@ void nsTreeContentView::CycleHeader(nsTr
         break;
     }
 
     nsAutoString hints;
     column->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
     sortdirection.Append(' ');
     sortdirection += hints;
 
-    XULWidgetSort(mRoot, sort, sortdirection);
+    XULWidgetSort(mTree, sort, sortdirection);
   }
 }
 
 NS_IMETHODIMP
 nsTreeContentView::CycleHeader(nsTreeColumn* aCol) {
   NS_ENSURE_ARG(aCol);
 
   ErrorResult rv;
@@ -744,20 +722,19 @@ void nsTreeContentView::AttributeChanged
                                          int32_t aNameSpaceID,
                                          nsAtom* aAttribute, int32_t aModType,
                                          const nsAttrValue* aOldValue) {
   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
   // Make sure this notification concerns us.
   // First check the tag to see if it's one that we care about.
-
-  if (mBoxObject && (aElement == mRoot || aElement == mBody)) {
-    mBoxObject->ClearStyleAndImageCaches();
-    mBoxObject->Invalidate();
+  if (aElement == mTree || aElement == mBody) {
+    mTree->ClearStyleAndImageCaches();
+    mTree->Invalidate();
   }
 
   // We don't consider non-XUL nodes.
   nsIContent* parent = nullptr;
   if (!aElement->IsXULElement() ||
       ((parent = aElement->GetParent()) && !parent->IsXULElement())) {
     return;
   }
@@ -782,94 +759,94 @@ void nsTreeContentView::AttributeChanged
                                    nsGkAtoms::treeseparator)) {
     bool hidden = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
                                         nsGkAtoms::_true, eCaseMatters);
 
     int32_t index = FindContent(aElement);
     if (hidden && index >= 0) {
       // Hide this row along with its children.
       int32_t count = RemoveRow(index);
-      if (mBoxObject) mBoxObject->RowCountChanged(index, -count);
+      if (mTree) mTree->RowCountChanged(index, -count);
     } else if (!hidden && index < 0) {
       // Show this row along with its children.
       nsCOMPtr<nsIContent> parent = aElement->GetParent();
       if (parent) {
         InsertRowFor(parent, aElement);
       }
     }
 
     return;
   }
 
   if (aElement->IsXULElement(nsGkAtoms::treecol)) {
     if (aAttribute == nsGkAtoms::properties) {
-      if (mBoxObject) {
+      if (mTree) {
         RefPtr<nsTreeColumns> cols;
-        mBoxObject->GetColumns(getter_AddRefs(cols));
+        mTree->GetColumns(getter_AddRefs(cols));
         if (cols) {
           RefPtr<nsTreeColumn> col = cols->GetColumnFor(aElement);
-          mBoxObject->InvalidateColumn(col);
+          mTree->InvalidateColumn(col);
         }
       }
     }
   } else if (aElement->IsXULElement(nsGkAtoms::treeitem)) {
     int32_t index = FindContent(aElement);
     if (index >= 0) {
       Row* row = mRows[index].get();
       if (aAttribute == nsGkAtoms::container) {
         bool isContainer =
             aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
                                   nsGkAtoms::_true, eCaseMatters);
         row->SetContainer(isContainer);
-        if (mBoxObject) mBoxObject->InvalidateRow(index);
+        if (mTree) mTree->InvalidateRow(index);
       } else if (aAttribute == nsGkAtoms::open) {
         bool isOpen = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
                                             nsGkAtoms::_true, eCaseMatters);
         bool wasOpen = row->IsOpen();
         if (!isOpen && wasOpen)
           CloseContainer(index);
         else if (isOpen && !wasOpen)
           OpenContainer(index);
       } else if (aAttribute == nsGkAtoms::empty) {
         bool isEmpty =
             aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
                                   nsGkAtoms::_true, eCaseMatters);
         row->SetEmpty(isEmpty);
-        if (mBoxObject) mBoxObject->InvalidateRow(index);
+        if (mTree) mTree->InvalidateRow(index);
       }
     }
   } else if (aElement->IsXULElement(nsGkAtoms::treeseparator)) {
     int32_t index = FindContent(aElement);
     if (index >= 0) {
-      if (aAttribute == nsGkAtoms::properties && mBoxObject) {
-        mBoxObject->InvalidateRow(index);
+      if (aAttribute == nsGkAtoms::properties && mTree) {
+        mTree->InvalidateRow(index);
       }
     }
   } else if (aElement->IsXULElement(nsGkAtoms::treerow)) {
     if (aAttribute == nsGkAtoms::properties) {
       nsCOMPtr<nsIContent> parent = aElement->GetParent();
       if (parent) {
         int32_t index = FindContent(parent);
-        if (index >= 0 && mBoxObject) {
-          mBoxObject->InvalidateRow(index);
+        if (index >= 0 && mTree) {
+          mTree->InvalidateRow(index);
         }
       }
     }
   } else if (aElement->IsXULElement(nsGkAtoms::treecell)) {
     if (aAttribute == nsGkAtoms::properties || aAttribute == nsGkAtoms::mode ||
         aAttribute == nsGkAtoms::src || aAttribute == nsGkAtoms::value ||
         aAttribute == nsGkAtoms::label) {
       nsIContent* parent = aElement->GetParent();
       if (parent) {
         nsCOMPtr<nsIContent> grandParent = parent->GetParent();
         if (grandParent && grandParent->IsXULElement()) {
           int32_t index = FindContent(grandParent);
-          if (index >= 0 && mBoxObject) {
+          if (index >= 0 && mTree) {
             // XXX Should we make an effort to invalidate only cell ?
-            mBoxObject->InvalidateRow(index);
+            mTree->InvalidateRow(index);
           }
         }
       }
     }
   }
 }
 
 void nsTreeContentView::ContentAppended(nsIContent* aFirstNewContent) {
@@ -907,33 +884,33 @@ void nsTreeContentView::ContentInserted(
   // Lots of codepaths under here that do all sorts of stuff, so be safe.
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 
   if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
     int32_t index = FindContent(container);
     if (index >= 0) {
       Row* row = mRows[index].get();
       row->SetEmpty(false);
-      if (mBoxObject) mBoxObject->InvalidateRow(index);
+      if (mTree) mTree->InvalidateRow(index);
       if (row->IsContainer() && row->IsOpen()) {
         int32_t count = EnsureSubtree(index);
-        if (mBoxObject) mBoxObject->RowCountChanged(index + 1, count);
+        if (mTree) mTree->RowCountChanged(index + 1, count);
       }
     }
   } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
                                         nsGkAtoms::treeseparator)) {
     InsertRowFor(container, aChild);
   } else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
     int32_t index = FindContent(container);
-    if (index >= 0 && mBoxObject) mBoxObject->InvalidateRow(index);
+    if (index >= 0 && mTree) mTree->InvalidateRow(index);
   } else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
     nsCOMPtr<nsIContent> parent = container->GetParent();
     if (parent) {
       int32_t index = FindContent(parent);
-      if (index >= 0 && mBoxObject) mBoxObject->InvalidateRow(index);
+      if (index >= 0 && mTree) mTree->InvalidateRow(index);
     }
   }
 }
 
 void nsTreeContentView::ContentRemoved(nsIContent* aChild,
                                        nsIContent* aPreviousSibling) {
   NS_ASSERTION(aChild, "null ptr");
 
@@ -964,36 +941,36 @@ void nsTreeContentView::ContentRemoved(n
 
   if (aChild->IsXULElement(nsGkAtoms::treechildren)) {
     int32_t index = FindContent(container);
     if (index >= 0) {
       Row* row = mRows[index].get();
       row->SetEmpty(true);
       int32_t count = RemoveSubtree(index);
       // Invalidate also the row to update twisty.
-      if (mBoxObject) {
-        mBoxObject->InvalidateRow(index);
-        mBoxObject->RowCountChanged(index + 1, -count);
+      if (mTree) {
+        mTree->InvalidateRow(index);
+        mTree->RowCountChanged(index + 1, -count);
       }
     }
   } else if (aChild->IsAnyOfXULElements(nsGkAtoms::treeitem,
                                         nsGkAtoms::treeseparator)) {
     int32_t index = FindContent(aChild);
     if (index >= 0) {
       int32_t count = RemoveRow(index);
-      if (mBoxObject) mBoxObject->RowCountChanged(index, -count);
+      if (mTree) mTree->RowCountChanged(index, -count);
     }
   } else if (aChild->IsXULElement(nsGkAtoms::treerow)) {
     int32_t index = FindContent(container);
-    if (index >= 0 && mBoxObject) mBoxObject->InvalidateRow(index);
+    if (index >= 0 && mTree) mTree->InvalidateRow(index);
   } else if (aChild->IsXULElement(nsGkAtoms::treecell)) {
     nsCOMPtr<nsIContent> parent = container->GetParent();
     if (parent) {
       int32_t index = FindContent(parent);
-      if (index >= 0 && mBoxObject) mBoxObject->InvalidateRow(index);
+      if (index >= 0 && mTree) mTree->InvalidateRow(index);
     }
   }
 }
 
 void nsTreeContentView::NodeWillBeDestroyed(const nsINode* aNode) {
   // XXXbz do we need this strong ref?  Do we drop refs to self in ClearRows?
   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   ClearRows();
@@ -1170,18 +1147,17 @@ void nsTreeContentView::InsertRowFor(nsI
     }
   }
 
   if (insertRow) {
     int32_t index = 0;
     GetIndexInSubtree(aParent, aChild, &index);
 
     int32_t count = InsertRow(grandParentIndex, index, aChild);
-    if (mBoxObject)
-      mBoxObject->RowCountChanged(grandParentIndex + index + 1, count);
+    if (mTree) mTree->RowCountChanged(grandParentIndex + index + 1, count);
   }
 }
 
 int32_t nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex,
                                      nsIContent* aContent) {
   AutoTArray<UniquePtr<Row>, 8> rows;
   if (aContent->IsXULElement(nsGkAtoms::treeitem)) {
     SerializeItem(aContent->AsElement(), aParentIndex, &aIndex, rows);
@@ -1216,44 +1192,43 @@ int32_t nsTreeContentView::RemoveRow(int
 
   UpdateParentIndexes(aIndex, 0, -count);
 
   return count;
 }
 
 void nsTreeContentView::ClearRows() {
   mRows.Clear();
-  mRoot = nullptr;
   mBody = nullptr;
   // Remove ourselves from mDocument's observers.
   if (mDocument) {
     mDocument->RemoveObserver(this);
     mDocument = nullptr;
   }
 }
 
 void nsTreeContentView::OpenContainer(int32_t aIndex) {
   Row* row = mRows[aIndex].get();
   row->SetOpen(true);
 
   int32_t count = EnsureSubtree(aIndex);
-  if (mBoxObject) {
-    mBoxObject->InvalidateRow(aIndex);
-    mBoxObject->RowCountChanged(aIndex + 1, count);
+  if (mTree) {
+    mTree->InvalidateRow(aIndex);
+    mTree->RowCountChanged(aIndex + 1, count);
   }
 }
 
 void nsTreeContentView::CloseContainer(int32_t aIndex) {
   Row* row = mRows[aIndex].get();
   row->SetOpen(false);
 
   int32_t count = RemoveSubtree(aIndex);
-  if (mBoxObject) {
-    mBoxObject->InvalidateRow(aIndex);
-    mBoxObject->RowCountChanged(aIndex + 1, -count);
+  if (mTree) {
+    mTree->InvalidateRow(aIndex);
+    mTree->RowCountChanged(aIndex + 1, -count);
   }
 }
 
 int32_t nsTreeContentView::FindContent(nsIContent* aContent) {
   for (uint32_t i = 0; i < mRows.Length(); i++) {
     if (mRows[i]->mContent == aContent) {
       return i;
     }
--- a/layout/xul/tree/nsTreeContentView.h
+++ b/layout/xul/tree/nsTreeContentView.h
@@ -5,32 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTreeContentView_h__
 #define nsTreeContentView_h__
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsStubDocumentObserver.h"
-#include "nsITreeBoxObject.h"
 #include "nsITreeView.h"
 #include "nsITreeSelection.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/UniquePtr.h"
 
 class nsSelection;
 class nsTreeColumn;
 class Row;
 
 namespace mozilla {
 namespace dom {
 class DataTransfer;
 class Document;
 class Element;
-class TreeBoxObject;
+class XULTreeElement;
 }  // namespace dom
 }  // namespace mozilla
 
 nsresult NS_NewTreeContentView(nsITreeView** aResult);
 
 class nsTreeContentView final : public nsITreeView,
                                 public nsStubDocumentObserver,
                                 public nsWrapperCache {
@@ -71,17 +70,17 @@ class nsTreeContentView final : public n
                       mozilla::ErrorResult& aError);
   int32_t GetLevel(int32_t aRow, mozilla::ErrorResult& aError);
   void GetImageSrc(int32_t aRow, nsTreeColumn& aColumn, nsAString& aSrc,
                    mozilla::ErrorResult& aError);
   void GetCellValue(int32_t aRow, nsTreeColumn& aColumn, nsAString& aValue,
                     mozilla::ErrorResult& aError);
   void GetCellText(int32_t aRow, nsTreeColumn& aColumn, nsAString& aText,
                    mozilla::ErrorResult& aError);
-  void SetTree(mozilla::dom::TreeBoxObject* aTree,
+  void SetTree(mozilla::dom::XULTreeElement* aTree,
                mozilla::ErrorResult& aError);
   void ToggleOpenState(int32_t aRow, mozilla::ErrorResult& aError);
   void CycleHeader(nsTreeColumn& aColumn, mozilla::ErrorResult& aError);
   void SelectionChanged() {}
   void CycleCell(int32_t aRow, nsTreeColumn& aColumn) {}
   bool IsEditable(int32_t aRow, nsTreeColumn& aColumn,
                   mozilla::ErrorResult& aError);
   void SetCellValue(int32_t aRow, nsTreeColumn& aColumn,
@@ -151,17 +150,16 @@ class nsTreeContentView final : public n
   void Drop(int32_t aRow, int32_t aOrientation, mozilla::ErrorResult& aError);
 
   // Content helpers.
   Element* GetCell(nsIContent* aContainer, nsTreeColumn& aCol);
 
  private:
   bool IsValidRowIndex(int32_t aRowIndex);
 
-  nsCOMPtr<nsITreeBoxObject> mBoxObject;
+  RefPtr<mozilla::dom::XULTreeElement> mTree;
   nsCOMPtr<nsITreeSelection> mSelection;
-  nsCOMPtr<Element> mRoot;
   nsCOMPtr<nsIContent> mBody;
   mozilla::dom::Document* mDocument;  // WEAK
   nsTArray<mozilla::UniquePtr<Row>> mRows;
 };
 
 #endif  // nsTreeContentView_h__
--- a/layout/xul/tree/nsTreeImageListener.cpp
+++ b/layout/xul/tree/nsTreeImageListener.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTreeImageListener.h"
-#include "nsITreeBoxObject.h"
+#include "XULTreeElement.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "nsIContent.h"
 #include "nsTreeColumns.h"
 
 NS_IMPL_ISUPPORTS(nsTreeImageListener, imgINotificationObserver)
 
 nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
@@ -75,17 +75,18 @@ void nsTreeImageListener::AddCell(int32_
 void nsTreeImageListener::Invalidate() {
   if (!mInvalidationSuppressed) {
     for (InvalidationArea* currArea = mInvalidationArea; currArea;
          currArea = currArea->GetNext()) {
       // Loop from min to max, invalidating each cell that was listening for
       // this image.
       for (int32_t i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
         if (mTreeFrame) {
-          nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
+          RefPtr<XULTreeElement> tree =
+              XULTreeElement::FromNodeOrNull(mTreeFrame->GetBaseElement());
           if (tree) {
             tree->InvalidateCell(i, currArea->GetCol());
           }
         }
       }
     }
   }
 }
--- a/layout/xul/tree/nsTreeSelection.cpp
+++ b/layout/xul/tree/nsTreeSelection.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/Element.h"
 #include "nsCOMPtr.h"
 #include "nsTreeSelection.h"
 #include "nsIBoxObject.h"
-#include "nsITreeBoxObject.h"
+#include "XULTreeElement.h"
 #include "nsITreeView.h"
 #include "nsString.h"
 #include "nsIContent.h"
 #include "nsNameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsComponentManagerUtils.h"
 #include "nsTreeColumns.h"
 
@@ -172,20 +172,20 @@ struct nsTreeRange {
     nsTreeRange* cur = aRange;
     while (cur) {
       aRanges.AppendElement(cur->mMin);
       aRanges.AppendElement(cur->mMax);
       cur = cur->mNext;
     }
   }
 
-  static void InvalidateRanges(nsITreeBoxObject* aTree,
+  static void InvalidateRanges(XULTreeElement* aTree,
                                nsTArray<int32_t>& aRanges) {
     if (aTree) {
-      nsCOMPtr<nsITreeBoxObject> tree = aTree;
+      RefPtr<nsXULElement> tree = aTree;
       for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
         aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
       }
     }
   }
 
   void Invalidate() {
     nsTArray<int32_t> ranges;
@@ -221,17 +221,17 @@ struct nsTreeRange {
       aRange->Connect(mPrev, this);
     else if (mNext)
       mNext->Insert(aRange);
     else
       aRange->Connect(this, nullptr);
   }
 };
 
-nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree)
+nsTreeSelection::nsTreeSelection(XULTreeElement* aTree)
     : mTree(aTree),
       mSuppressed(false),
       mCurrentIndex(-1),
       mShiftSelectPivot(-1),
       mFirstRange(nullptr) {}
 
 nsTreeSelection::~nsTreeSelection() {
   delete mFirstRange;
@@ -244,44 +244,38 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSe
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
   NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
   NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject** aTree) {
+NS_IMETHODIMP nsTreeSelection::GetTree(XULTreeElement** aTree) {
   NS_IF_ADDREF(*aTree = mTree);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject* aTree) {
+NS_IMETHODIMP nsTreeSelection::SetTree(XULTreeElement* aTree) {
   if (mSelectTimer) {
     mSelectTimer->Cancel();
     mSelectTimer = nullptr;
   }
 
-  // Make sure aTree really implements nsITreeBoxObject and nsIBoxObject!
-  nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree);
-  mTree = do_QueryInterface(bo);
-  NS_ENSURE_STATE(mTree == aTree);
+  mTree = aTree;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle) {
-  nsCOMPtr<nsIContent> content = GetContent();
-  if (!content) {
+  if (!mTree) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  *aSingle =
-      content->IsElement() && content->AsElement()->AttrValueIs(
-                                  kNameSpaceID_None, nsGkAtoms::seltype,
-                                  NS_LITERAL_STRING("single"), eCaseMatters);
+  *aSingle = mTree->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype,
+                                NS_LITERAL_STRING("single"), eCaseMatters);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult) {
   if (mFirstRange)
     *aResult = mFirstRange->Contains(aIndex);
   else
@@ -297,20 +291,18 @@ NS_IMETHODIMP nsTreeSelection::TimedSele
   nsresult rv = Select(aIndex);
   if (NS_FAILED(rv)) return rv;
 
   if (aMsec != -1) {
     mSuppressed = suppressSelect;
     if (!mSuppressed) {
       if (mSelectTimer) mSelectTimer->Cancel();
 
-      nsIEventTarget* target = nullptr;
-      if (nsCOMPtr<nsIContent> content = GetContent()) {
-        target = content->OwnerDoc()->EventTargetFor(TaskCategory::Other);
-      }
+      nsIEventTarget* target =
+          mTree->OwnerDoc()->EventTargetFor(TaskCategory::Other);
       NS_NewTimerWithFuncCallback(getter_AddRefs(mSelectTimer), SelectCallback,
                                   this, aMsec, nsITimer::TYPE_ONE_SHOT,
                                   "nsTreeSelection::SelectCallback", target);
     }
   }
 
   return NS_OK;
 }
@@ -567,29 +559,23 @@ NS_IMETHODIMP nsTreeSelection::SetCurren
   if (mCurrentIndex != -1 && mTree) mTree->InvalidateRow(mCurrentIndex);
 
   mCurrentIndex = aIndex;
   if (!mTree) return NS_OK;
 
   if (aIndex != -1) mTree->InvalidateRow(aIndex);
 
   // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
-  nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
-  NS_ASSERTION(boxObject, "no box object!");
-  if (!boxObject) return NS_ERROR_UNEXPECTED;
-  RefPtr<dom::Element> treeElt;
-  boxObject->GetElement(getter_AddRefs(treeElt));
-
-  NS_ENSURE_STATE(treeElt);
+  NS_ENSURE_STATE(mTree);
 
   NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
   NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
-      treeElt, (aIndex != -1 ? DOMMenuItemActive : DOMMenuItemInactive),
+      mTree, (aIndex != -1 ? DOMMenuItemActive : DOMMenuItemInactive),
       CanBubble::eYes, ChromeOnlyDispatch::eNo);
   return asyncDispatcher->PostDOMEvent();
 }
 
 #define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
   {                                                                         \
     int32_t start = macro_start;                                            \
     int32_t end = macro_end;                                                \
@@ -708,52 +694,33 @@ NS_IMETHODIMP
 nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex) {
   *aIndex = mShiftSelectPivot;
   return NS_OK;
 }
 
 nsresult nsTreeSelection::FireOnSelectHandler() {
   if (mSuppressed || !mTree) return NS_OK;
 
-  nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
-  NS_ASSERTION(boxObject, "no box object!");
-  if (!boxObject) return NS_ERROR_UNEXPECTED;
-  RefPtr<dom::Element> elt;
-  boxObject->GetElement(getter_AddRefs(elt));
-  NS_ENSURE_STATE(elt);
-
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
-      new AsyncEventDispatcher(elt, NS_LITERAL_STRING("select"),
+      new AsyncEventDispatcher(mTree, NS_LITERAL_STRING("select"),
                                CanBubble::eYes, ChromeOnlyDispatch::eNo);
   asyncDispatcher->RunDOMEventWhenSafe();
   return NS_OK;
 }
 
 void nsTreeSelection::SelectCallback(nsITimer* aTimer, void* aClosure) {
   RefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
   if (self) {
     self->FireOnSelectHandler();
     aTimer->Cancel();
     self->mSelectTimer = nullptr;
   }
 }
 
-already_AddRefed<nsIContent> nsTreeSelection::GetContent() {
-  if (!mTree) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
-
-  RefPtr<dom::Element> element;
-  boxObject->GetElement(getter_AddRefs(element));
-  return element.forget();
-}
-
 ///////////////////////////////////////////////////////////////////////////////////
 
-nsresult NS_NewTreeSelection(nsITreeBoxObject* aTree,
+nsresult NS_NewTreeSelection(XULTreeElement* aTree,
                              nsITreeSelection** aResult) {
   *aResult = new nsTreeSelection(aTree);
   if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
   NS_ADDREF(*aResult);
   return NS_OK;
 }
--- a/layout/xul/tree/nsTreeSelection.h
+++ b/layout/xul/tree/nsTreeSelection.h
@@ -6,24 +6,24 @@
 
 #ifndef nsTreeSelection_h__
 #define nsTreeSelection_h__
 
 #include "nsITreeSelection.h"
 #include "nsITimer.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
+#include "XULTreeElement.h"
 
-class nsITreeBoxObject;
 class nsTreeColumn;
 struct nsTreeRange;
 
 class nsTreeSelection final : public nsINativeTreeSelection {
  public:
-  explicit nsTreeSelection(nsITreeBoxObject* aTree);
+  explicit nsTreeSelection(mozilla::dom::XULTreeElement* aTree);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsTreeSelection)
   NS_DECL_NSITREESELECTION
 
   // nsINativeTreeSelection: Untrusted code can use us
   NS_IMETHOD EnsureNative() override { return NS_OK; }
 
@@ -31,30 +31,26 @@ class nsTreeSelection final : public nsI
 
  protected:
   ~nsTreeSelection();
 
   nsresult FireOnSelectHandler();
   static void SelectCallback(nsITimer* aTimer, void* aClosure);
 
  protected:
-  // Helper function to get the content node associated with mTree.
-  already_AddRefed<nsIContent> GetContent();
-
-  // Members
-  nsCOMPtr<nsITreeBoxObject> mTree;  // The tree will hold on to us through the
-                                     // view and let go when it dies.
+  // The tree will hold on to us through the view and let go when it dies.
+  RefPtr<mozilla::dom::XULTreeElement> mTree;
 
   bool mSuppressed;       // Whether or not we should be firing onselect events.
   int32_t mCurrentIndex;  // The item to draw the rect around. The last one
                           // clicked, etc.
   int32_t mShiftSelectPivot;  // Used when multiple SHIFT+selects are performed
                               // to pivot on.
 
   nsTreeRange* mFirstRange;  // Our list of ranges.
 
   nsCOMPtr<nsITimer> mSelectTimer;
 };
 
-nsresult NS_NewTreeSelection(nsITreeBoxObject* aTree,
+nsresult NS_NewTreeSelection(mozilla::dom::XULTreeElement* aTree,
                              nsITreeSelection** aResult);
 
 #endif
--- a/security/manager/pki/nsASN1Tree.cpp
+++ b/security/manager/pki/nsASN1Tree.cpp
@@ -250,17 +250,17 @@ NS_IMETHODIMP
 nsNSSASN1Tree::GetDisplayData(uint32_t index, nsAString& _retval) {
   myNode* n = FindNodeFromIndex(index);
   if (!n) return NS_ERROR_FAILURE;
 
   return n->obj->GetDisplayValue(_retval);
 }
 
 NS_IMETHODIMP
-nsNSSASN1Tree::SetTree(nsITreeBoxObject* tree) {
+nsNSSASN1Tree::SetTree(mozilla::dom::XULTreeElement* tree) {
   // Note: |tree| is allowed to be null.
   mTree = tree;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSASN1Tree::ToggleOpenState(int32_t index) {
   NS_ENSURE_ARG_MIN(index, 0);
--- a/security/manager/pki/nsASN1Tree.h
+++ b/security/manager/pki/nsASN1Tree.h
@@ -5,19 +5,19 @@
 #define _NSSASNTREE_H_
 
 #include "nscore.h"
 #include "nsIX509Cert.h"
 #include "nsIASN1Tree.h"
 #include "nsIASN1Object.h"
 #include "nsIASN1Sequence.h"
 #include "nsITreeView.h"
-#include "nsITreeBoxObject.h"
 #include "nsITreeSelection.h"
 #include "nsCOMPtr.h"
+#include "mozilla/dom/XULTreeElement.h"
 
 // 4bfaa9f0-1dd2-11b2-afae-a82cbaa0b606
 #define NS_NSSASN1OUTINER_CID                        \
   {                                                  \
     0x4bfaa9f0, 0x1dd2, 0x11b2, {                    \
       0xaf, 0xae, 0xa8, 0x2c, 0xba, 0xa0, 0xb6, 0x06 \
     }                                                \
   }
@@ -43,17 +43,17 @@ class nsNSSASN1Tree : public nsIASN1Tree
 
     myNode() { child = next = parent = nullptr; }
   };
 
   myNode *mTopNode;
 
   nsCOMPtr<nsIASN1Object> mASN1Object;
   nsCOMPtr<nsITreeSelection> mSelection;
-  nsCOMPtr<nsITreeBoxObject> mTree;
+  RefPtr<mozilla::dom::XULTreeElement> mTree;
 
   void InitNodes();
   void InitChildsRecursively(myNode *n);
 
   void ClearNodes();
   void ClearNodesRecursively(myNode *n);
 
   int32_t CountVisibleNodes(myNode *n);
--- a/security/manager/ssl/nsCertTree.cpp
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -994,17 +994,17 @@ nsCertTree::GetCellText(int32_t row, nsT
     NS_ENSURE_SUCCESS(rv, rv);
     text->SetData(_retval);
     mCellText->ReplaceElementAt(text, arrayIndex);
   }
   return rv;
 }
 
 NS_IMETHODIMP
-nsCertTree::SetTree(nsITreeBoxObject *tree) {
+nsCertTree::SetTree(mozilla::dom::XULTreeElement *tree) {
   mTree = tree;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCertTree::ToggleOpenState(int32_t index) {
   if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
   treeArrayEl *el = GetThreadDescAtIndex(index);
--- a/security/manager/ssl/nsCertTree.h
+++ b/security/manager/ssl/nsCertTree.h
@@ -4,25 +4,25 @@
 
 #ifndef _NS_CERTTREE_H_
 #define _NS_CERTTREE_H_
 
 #include "nsCOMPtr.h"
 #include "nsIServiceManager.h"
 #include "nsICertTree.h"
 #include "nsITreeView.h"
-#include "nsITreeBoxObject.h"
 #include "nsITreeSelection.h"
 #include "nsIMutableArray.h"
 #include "nsNSSComponent.h"
 #include "nsTArray.h"
 #include "PLDHashTable.h"
 #include "nsIX509CertDB.h"
 #include "nsCertOverrideService.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/XULTreeElement.h"
 
 typedef struct treeArrayElStr treeArrayEl;
 
 struct CompareCacheHashEntry {
   enum { max_criterions = 3 };
   CompareCacheHashEntry();
 
   void *key;  // no ownership
@@ -111,17 +111,17 @@ class nsCertTree : public nsICertTree {
   static int32_t CmpEmailCert(void *cache, nsIX509Cert *a, nsIX509Cert *b);
   nsCertCompareFunc GetCompareFuncFromCertType(uint32_t aType);
   int32_t CountOrganizations();
 
  private:
   static const uint32_t kInitialCacheLength = 64;
 
   nsTArray<RefPtr<nsCertTreeDispInfo> > mDispInfo;
-  nsCOMPtr<nsITreeBoxObject> mTree;
+  RefPtr<mozilla::dom::XULTreeElement> mTree;
   nsCOMPtr<nsITreeSelection> mSelection;
   treeArrayEl *mTreeArray;
   int32_t mNumOrgs;
   int32_t mNumRows;
   PLDHashTable mCompareCache;
   nsCOMPtr<nsICertOverrideService> mOverrideService;
   RefPtr<nsCertOverrideService> mOriginalOverrideService;
 
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -44,17 +44,17 @@ let signonReloadDisplay = {
             return;
           }
           signons.length = 0;
           LoadSignons();
           // apply the filter if needed
           if (filterField && filterField.value != "") {
             FilterPasswords();
           }
-          signonsTree.treeBoxObject.ensureRowIsVisible(signonsTree.view.selection.currentIndex);
+          signonsTree.ensureRowIsVisible(signonsTree.view.selection.currentIndex);
           break;
       }
       Services.obs.notifyObservers(null, "passwordmgr-dialog-updated");
     }
   },
 };
 
 // Formatter for localization.
@@ -186,17 +186,17 @@ let signonsTreeView = {
     function _editLogin(field) {
       if (value == table[row][field]) {
         return;
       }
       let existingLogin = table[row].clone();
       table[row][field] = value;
       table[row].timePasswordChanged = Date.now();
       Services.logins.modifyLogin(existingLogin, table[row]);
-      signonsTree.treeBoxObject.invalidateRow(row);
+      signonsTree.invalidateRow(row);
     }
 
     if (col.id == "userCol") {
      _editLogin("username");
 
     } else if (col.id == "passwordCol") {
       if (!value) {
         return;
@@ -259,19 +259,19 @@ function SortTree(column, ascending) {
         signonsTree.view.selection.select(s);
         selectedRow = s;
         break;
       }
     }
   }
 
   // display the results
-  signonsTree.treeBoxObject.invalidate();
+  signonsTree.invalidate();
   if (selectedRow >= 0) {
-    signonsTree.treeBoxObject.ensureRowIsVisible(selectedRow);
+    signonsTree.ensureRowIsVisible(selectedRow);
   }
 }
 
 function LoadSignons() {
   // loads signons into table
   try {
     signons = Services.logins.getAllLogins();
   } catch (e) {
@@ -353,17 +353,17 @@ function DeleteSignon() {
   for (let j = 0; j < table.length; j++) {
     if (table[j] == null) {
       let k = j;
       while ((k < table.length) && (table[k] == null)) {
         k++;
       }
       table.splice(j, k - j);
       view.rowCount -= k - j;
-      tree.treeBoxObject.rowCountChanged(j, j - k);
+      tree.rowCountChanged(j, j - k);
     }
   }
 
   // update selection and/or buttons
   if (table.length) {
     // update selection
     let nextSelection = (selections[0] < table.length) ? selections[0] : table.length - 1;
     tree.view.selection.select(nextSelection);
@@ -397,19 +397,18 @@ function DeleteAllSignons() {
   table.length = 0;
 
   // clear out selections
   view.selection.select(-1);
 
   // update the tree view and notify the tree
   view.rowCount = 0;
 
-  let box = signonsTree.treeBoxObject;
-  box.rowCountChanged(0, -deletedSignons.length);
-  box.invalidate();
+  signonsTree.rowCountChanged(0, -deletedSignons.length);
+  signonsTree.invalidate();
 
   // disable buttons
   removeButton.setAttribute("disabled", "true");
   removeAllButton.setAttribute("disabled", "true");
   FinalizeSignonDeletions(syncNeeded);
   Services.telemetry.getHistogramById("PWMGR_MANAGE_DELETED_ALL").add(1);
   Services.obs.notifyObservers(null, "weave:telemetry:histogram", "PWMGR_MANAGE_DELETED_ALL");
 }
@@ -511,17 +510,17 @@ function SignonColumnSort(column) {
                                           "ascending" : "descending");
 }
 
 function SignonClearFilter() {
   let singleSelection = (signonsTreeView.selection.count == 1);
 
   // Clear the Tree Display
   signonsTreeView.rowCount = 0;
-  signonsTree.treeBoxObject.rowCountChanged(0, -signonsTreeView._filterSet.length);
+  signonsTree.rowCountChanged(0, -signonsTreeView._filterSet.length);
   signonsTreeView._filterSet = [];
 
   // Just reload the list to make sure deletions are respected
   LoadSignons();
 
   // Restore selection
   if (singleSelection) {
     signonsTreeView.selection.clearSelection();
@@ -590,20 +589,20 @@ function FilterPasswords() {
     // enter Filtered mode.
     SignonSaveState();
   }
   signonsTreeView._filterSet = newFilterSet;
 
   // Clear the display
   let oldRowCount = signonsTreeView.rowCount;
   signonsTreeView.rowCount = 0;
-  signonsTree.treeBoxObject.rowCountChanged(0, -oldRowCount);
+  signonsTree.rowCountChanged(0, -oldRowCount);
   // Set up the filtered display
   signonsTreeView.rowCount = signonsTreeView._filterSet.length;
-  signonsTree.treeBoxObject.rowCountChanged(0, signonsTreeView.rowCount);
+  signonsTree.rowCountChanged(0, signonsTreeView.rowCount);
 
   // if the view is not empty then select the first item
   if (signonsTreeView.rowCount > 0)
     signonsTreeView.selection.select(0);
 
   signonsIntro.textContent = kSignonBundle.getString("loginsDescriptionFiltered");
   removeAllButton.setAttribute("label", kSignonBundle.getString("removeAllShown.label"));
   removeAllButton.setAttribute("accesskey", kSignonBundle.getString("removeAllShown.accesskey"));
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_editing.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_editing.js
@@ -17,18 +17,17 @@ function getUsername(row) {
   return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("userCol"));
 }
 
 function getPassword(row) {
   return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("passwordCol"));
 }
 
 function synthesizeDblClickOnCell(aTree, column, row) {
-  let tbo = aTree.treeBoxObject;
-  let rect = tbo.getCoordsForCellItem(row, aTree.columns[column], "text");
+  let rect = aTree.getCoordsForCellItem(row, aTree.columns[column], "text");
   let x = rect.x + rect.width / 2;
   let y = rect.y + rect.height / 2;
   // Simulate the double click.
   EventUtils.synthesizeMouse(aTree.body, x, y, { clickCount: 2 },
                              aTree.ownerGlobal);
 }
 
 async function togglePasswords() {
--- a/toolkit/content/tests/chrome/test_contextmenu_list.xul
+++ b/toolkit/content/tests/chrome/test_contextmenu_list.xul
@@ -240,17 +240,17 @@ function checkPopup()
          "list selection keyboard top");
     }
     else {
       var tree = $("tree");
       var bodyrect = $("treechildren").getBoundingClientRect();
       isRoundedX(menurect.left, bodyrect.left + contextMenuOffsetX,
          "tree selection keyboard left");
       isRoundedY(menurect.top, bodyrect.top +
-         tree.treeBoxObject.rowHeight * 3 + contextMenuOffsetY,
+         tree.rowHeight * 3 + contextMenuOffsetY,
          "tree selection keyboard top");
     }
   }
   else if (gTestId == 1) {
     // activating a context menu with the mouse from position (7, 4).
     var elementrect = $(gTestElement).getBoundingClientRect();
     isRoundedX(menurect.left, elementrect.left + 7 + contextMenuOffsetX,
        gTestElement + " mouse left");
--- a/toolkit/content/tests/chrome/test_popup_tree.xul
+++ b/toolkit/content/tests/chrome/test_popup_tree.xul
@@ -44,17 +44,17 @@ function runTests()
 
 function treeClick()
 {
   var tree = $("tree");
   is(tree.currentIndex, -1, "selectedIndex before click");
   synthesizeMouseExpectEvent($("treechildren"), 2, 2, { }, $("treechildren"), "click", "");
   is(tree.currentIndex, 0, "selectedIndex after click");
 
-  var rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns.address, "");
+  var rect = tree.getCoordsForCellItem(1, tree.columns.address, "");
   synthesizeMouseExpectEvent($("treechildren"), rect.x, rect.y + 2,
                              { }, $("treechildren"), "click", "");
   is(tree.currentIndex, 1, "selectedIndex after second click " + rect.x + "," + rect.y);
 
   $("panel").hidePopup();
 }
 
 ]]>
--- a/toolkit/content/tests/chrome/test_tree_view.xul
+++ b/toolkit/content/tests/chrome/test_tree_view.xul
@@ -77,18 +77,18 @@ function getCustomTreeViewCellInfo()
 
   return obj;
 }
 
 function init()
 {
   var tree = document.getElementById("tree-view");
   tree.view = view;
-  tree.treeBoxObject.ensureRowIsVisible(0);
-  is(tree.treeBoxObject.getFirstVisibleRow(), 0, "first visible after ensureRowIsVisible on load");
+  tree.ensureRowIsVisible(0);
+  is(tree.getFirstVisibleRow(), 0, "first visible after ensureRowIsVisible on load");
   tree.setAttribute("rows", "4");
 
   setTimeout(testtag_tree, 0, "tree-view", "treechildren-view", "multiple", "simple", "tree view");
 }
 
 ]]>
 </script>
 
--- a/toolkit/content/tests/chrome/window_panel.xul
+++ b/toolkit/content/tests/chrome/window_panel.xul
@@ -108,22 +108,22 @@ function createPanel(attrs)
   return document.documentElement.appendChild(panel);
 }
 
 function checkTreeCoords()
 {
   var tree = $("tree");
   var treechildren = $("treechildren");
   tree.currentIndex = 0;
-  tree.treeBoxObject.scrollToRow(0);
-  synthesizeMouse(treechildren, 10, tree.treeBoxObject.rowHeight + 2, { });
+  tree.scrollToRow(0);
+  synthesizeMouse(treechildren, 10, tree.rowHeight + 2, { });
   is(tree.currentIndex, 1, "tree selection");
 
-  tree.treeBoxObject.scrollToRow(2);
-  synthesizeMouse(treechildren, 10, tree.treeBoxObject.rowHeight + 2, { });
+  tree.scrollToRow(2);
+  synthesizeMouse(treechildren, 10, tree.rowHeight + 2, { });
   is(tree.currentIndex, 3, "tree selection after scroll");
 }
 
 var tests = [
   {
     testname: "normal panel",
     attrs: { },
     test: function(panel) {
--- a/toolkit/content/tests/widgets/tree_shared.js
+++ b/toolkit/content/tests/widgets/tree_shared.js
@@ -349,58 +349,58 @@ function testtag_tree_TreeSelection_UI(t
 
   // pressing down while the last row is selected should not fire a select event,
   // as the selection won't have changed. Also the view is not scrolled in this case.
   selection.select(7);
   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end");
   testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0);
 
   // pressing keys while at the edge of the visible rows should scroll the list
-  tree.treeBoxObject.scrollToRow(4);
+  tree.scrollToRow(4);
   selection.select(4);
   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll");
-  is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with scroll");
+  is(tree.getFirstVisibleRow(), 3, testid + "key up with scroll");
 
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(3);
   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down with scroll");
-  is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with scroll");
+  is(tree.getFirstVisibleRow(), 1, testid + "key down with scroll");
 
   // accel key and cursor movement adjust currentIndex but should not change
   // the selection. In single selection mode, the selection will not change,
   // but instead will just scroll up or down a line
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(1);
   synthesizeKeyExpectEvent("VK_DOWN", { accelKey: true }, tree, "!select", "key down with accel");
   testtag_tree_TreeSelection_State(tree, testid + "key down with accel", multiple ? 2 : 1, [1]);
   if (!multiple)
-    is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
+    is(tree.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
 
-  tree.treeBoxObject.scrollToRow(4);
+  tree.scrollToRow(4);
   selection.select(4);
   synthesizeKeyExpectEvent("VK_UP", { accelKey: true }, tree, "!select", "key up with accel");
   testtag_tree_TreeSelection_State(tree, testid + "key up with accel", multiple ? 3 : 4, [4]);
   if (!multiple)
-    is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
+    is(tree.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
 
   // do this three times, one for each state of pageUpOrDownMovesSelection,
   // and then once with the accel key pressed
   for (let t = 0; t < 3; t++) {
     let testidmod = "";
     if (t == 2)
       testidmod = " with accel";
     else if (t == 1)
       testidmod = " rev";
     var keymod = (t == 2) ? { accelKey: true } : { };
 
     var moveselection = tree.pageUpOrDownMovesSelection;
     if (t == 2)
       moveselection = !moveselection;
 
-    tree.treeBoxObject.scrollToRow(4);
+    tree.scrollToRow(4);
     selection.currentIndex = 6;
     selection.select(6);
     var expected = moveselection ? 4 : 6;
     synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up");
     testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
                                      expected, [expected], moveselection ? 4 : 0);
 
     expected = moveselection ? 0 : 6;
@@ -408,17 +408,17 @@ function testtag_tree_TreeSelection_UI(t
     testtag_tree_TreeSelection_State(tree, testid + "key page up again" + testidmod,
                                      expected, [expected], 0);
 
     expected = moveselection ? 0 : 6;
     synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up at start");
     testtag_tree_TreeSelection_State(tree, testid + "key page up at start" + testidmod,
                                      expected, [expected], 0);
 
-    tree.treeBoxObject.scrollToRow(0);
+    tree.scrollToRow(0);
     selection.currentIndex = 1;
     selection.select(1);
     expected = moveselection ? 3 : 1;
     synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down");
     testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
                                      expected, [expected], moveselection ? 0 : 4);
 
     expected = moveselection ? 7 : 1;
@@ -430,33 +430,33 @@ function testtag_tree_TreeSelection_UI(t
     synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down at start");
     testtag_tree_TreeSelection_State(tree, testid + "key page down at start" + testidmod,
                                      expected, [expected], 4);
 
     if (t < 2)
       tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   }
 
-  tree.treeBoxObject.scrollToRow(4);
+  tree.scrollToRow(4);
   selection.select(6);
   synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
   testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0);
 
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(1);
   synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
   testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4);
 
   // in single selection mode, the selection doesn't change in this case
-  tree.treeBoxObject.scrollToRow(4);
+  tree.scrollToRow(4);
   selection.select(6);
   synthesizeKeyExpectEvent("VK_HOME", { accelKey: true }, tree, "!select", "key home with accel");
   testtag_tree_TreeSelection_State(tree, testid + "key home with accel", multiple ? 0 : 6, [6], 0);
 
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(1);
   synthesizeKeyExpectEvent("VK_END", { accelKey: true }, tree, "!select", "key end with accel");
   testtag_tree_TreeSelection_State(tree, testid + "key end with accel", multiple ? 7 : 1, [1], 4);
 
   // next, test cursor navigation with selection. Here the select event will be fired
   selection.select(1);
   var eventExpected = multiple ? "select" : "!select";
   synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, eventExpected, "key shift down to select");
@@ -482,17 +482,17 @@ function testtag_tree_TreeSelection_UI(t
   // and the selection always changes
   var lastidx = tree.view.rowCount - 1;
   for (let t = 0; t < 2; t++) {
     let testidmod = (t == 0) ? "" : " rev";
 
     // If the top or bottom visible row is the current row, pressing shift and
     // page down / page up selects one page up or one page down. Otherwise, the
     // selection is made to the top or bottom of the visible area.
-    tree.treeBoxObject.scrollToRow(lastidx - 3);
+    tree.scrollToRow(lastidx - 3);
     selection.currentIndex = 6;
     selection.select(6);
     synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected, "key shift page up");
     testtag_tree_TreeSelection_State(tree, testid + "key shift page up" + testidmod,
                                      multiple ? 4 : 6, multiple ? [4, 5, 6] : [6]);
     if (multiple) {
       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up again");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page up again" + testidmod,
@@ -502,17 +502,17 @@ function testtag_tree_TreeSelection_UI(t
       testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start" + testidmod,
                                        0, [0, 1, 2, 3, 4, 5, 6]);
       // deselect by paging down again
       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down deselect");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page down deselect" + testidmod,
                                        3, [3, 4, 5, 6]);
     }
 
-    tree.treeBoxObject.scrollToRow(1);
+    tree.scrollToRow(1);
     selection.currentIndex = 2;
     selection.select(2);
     synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected, "key shift page down");
     testtag_tree_TreeSelection_State(tree, testid + "key shift page down" + testidmod,
                                      multiple ? 4 : 2, multiple ? [2, 3, 4] : [2]);
     if (multiple) {
       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down again");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page down again" + testidmod,
@@ -523,61 +523,61 @@ function testtag_tree_TreeSelection_UI(t
       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up deselect");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page up deselect" + testidmod,
                                        4, [2, 3, 4]);
     }
 
     // test when page down / page up is pressed when the view is scrolled such
     // that the selection is not visible
     if (multiple) {
-      tree.treeBoxObject.scrollToRow(3);
+      tree.scrollToRow(3);
       selection.currentIndex = 1;
       selection.select(1);
       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected,
                                "key shift page down with view scrolled down");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page down with view scrolled down" + testidmod,
                                        6, [1, 2, 3, 4, 5, 6], 3);
 
-      tree.treeBoxObject.scrollToRow(2);
+      tree.scrollToRow(2);
       selection.currentIndex = 6;
       selection.select(6);
       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected,
                                "key shift page up with view scrolled up");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page up with view scrolled up" + testidmod,
                                        2, [2, 3, 4, 5, 6], 2);
 
-      tree.treeBoxObject.scrollToRow(2);
+      tree.scrollToRow(2);
       selection.currentIndex = 0;
       selection.select(0);
       // don't expect the select event, as the selection won't have changed
       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select",
                                "key shift page up at start with view scrolled down");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start with view scrolled down" + testidmod,
                                        0, [0], 0);
 
-      tree.treeBoxObject.scrollToRow(0);
+      tree.scrollToRow(0);
       selection.currentIndex = 7;
       selection.select(7);
       // don't expect the select event, as the selection won't have changed
       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select",
                                "key shift page down at end with view scrolled up");
       testtag_tree_TreeSelection_State(tree, testid + "key shift page down at end with view scrolled up" + testidmod,
                                        7, [7], 4);
     }
 
     tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   }
 
-  tree.treeBoxObject.scrollToRow(4);
+  tree.scrollToRow(4);
   selection.select(5);
   synthesizeKeyExpectEvent("VK_HOME", { shiftKey: true }, tree, eventExpected, "key shift home");
   testtag_tree_TreeSelection_State(tree, testid + "key shift home",
                                    multiple ? 0 : 5, multiple ? [0, 1, 2, 3, 4, 5] : [5], multiple ? 0 : 4);
 
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(3);
   synthesizeKeyExpectEvent("VK_END", { shiftKey: true }, tree, eventExpected, "key shift end");
   testtag_tree_TreeSelection_State(tree, testid + "key shift end",
                                    multiple ? 7 : 3, multiple ? [3, 4, 5, 6, 7] : [3], multiple ? 4 : 0);
 
   // pressing space selects a row, pressing accel + space unselects a row
   selection.select(2);
   selection.currentIndex = 4;
@@ -586,17 +586,17 @@ function testtag_tree_TreeSelection_UI(t
   testtag_tree_TreeSelection_State(tree, testid + "key space on", 4, multiple ? [2, 4] : [2]);
 
   if (multiple) {
     synthesizeKeyExpectEvent(" ", { accelKey: true }, tree, "select", "key space off");
     testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]);
   }
 
   // check that clicking on a row selects it
-  tree.treeBoxObject.scrollToRow(0);
+  tree.scrollToRow(0);
   selection.select(2);
   selection.currentIndex = 2;
   if (0) { // XXXndeakin disable these tests for now
     mouseOnCell(tree, 1, tree.columns[1], "mouse on row");
     testtag_tree_TreeSelection_State(tree, testid + "mouse on row", 1, [1], 0, null);
   }
 
   // restore the scroll position to the start of the page
@@ -843,17 +843,17 @@ function testtag_tree_TreeView_rows_sort
 // viewidx is the row that should be visible at the top of the tree
 function testtag_tree_TreeSelection_State(tree, testid, current, selected, viewidx) {
   var selection = tree.view.selection;
 
   is(selection.count, selected.length, testid + " count");
   is(tree.currentIndex, current, testid + " currentIndex");
   is(selection.currentIndex, current, testid + " TreeSelection currentIndex");
   if (viewidx !== null && viewidx !== undefined)
-    is(tree.treeBoxObject.getFirstVisibleRow(), viewidx, testid + " first visible row");
+    is(tree.getFirstVisibleRow(), viewidx, testid + " first visible row");
 
   var actualSelected = [];
   var count = tree.view.rowCount;
   for (var s = 0; s < count; s++) {
     if (selection.isSelected(s))
       actualSelected.push(s);
   }
 
@@ -940,48 +940,48 @@ function testtag_tree_column_reorder() {
 
 function testtag_tree_wheel(aTree) {
   const deltaModes = [
     WheelEvent.DOM_DELTA_PIXEL,  // 0
     WheelEvent.DOM_DELTA_LINE,   // 1
     WheelEvent.DOM_DELTA_PAGE,    // 2
   ];
   function helper(aStart, aDelta, aIntDelta, aDeltaMode) {
-    aTree.treeBoxObject.scrollToRow(aStart);
+    aTree.scrollToRow(aStart);
     var expected;
     if (!aIntDelta) {
       expected = aStart;
     } else if (aDeltaMode != WheelEvent.DOM_DELTA_PAGE) {
       expected = aStart + aIntDelta;
     } else if (aIntDelta > 0) {
-      expected = aStart + aTree.treeBoxObject.getPageLength();
+      expected = aStart + aTree.getPageLength();
     } else {
-      expected = aStart - aTree.treeBoxObject.getPageLength();
+      expected = aStart - aTree.getPageLength();
     }
 
     if (expected < 0) {
       expected = 0;
     }
-    if (expected > aTree.view.rowCount - aTree.treeBoxObject.getPageLength()) {
-      expected = aTree.view.rowCount - aTree.treeBoxObject.getPageLength();
+    if (expected > aTree.view.rowCount - aTree.getPageLength()) {
+      expected = aTree.view.rowCount - aTree.getPageLength();
     }
     synthesizeWheel(aTree.body, 1, 1,
                     { deltaMode: aDeltaMode, deltaY: aDelta,
                       lineOrPageDeltaY: aIntDelta });
-    is(aTree.treeBoxObject.getFirstVisibleRow(), expected,
+    is(aTree.getFirstVisibleRow(), expected,
          "testtag_tree_wheel: vertical, starting " + aStart +
            " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
            " aDeltaMode " + aDeltaMode);
 
-    aTree.treeBoxObject.scrollToRow(aStart);
+    aTree.scrollToRow(aStart);
     // Check that horizontal scrolling has no effect
     synthesizeWheel(aTree.body, 1, 1,
                     { deltaMode: aDeltaMode, deltaX: aDelta,
                       lineOrPageDeltaX: aIntDelta });
-    is(aTree.treeBoxObject.getFirstVisibleRow(), aStart,
+    is(aTree.getFirstVisibleRow(), aStart,
          "testtag_tree_wheel: horizontal, starting " + aStart +
            " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
            " aDeltaMode " + aDeltaMode);
   }
 
   var defaultPrevented = 0;
 
   function wheelListener(event) {
@@ -1070,17 +1070,17 @@ function checkColumns(aTree, aReference,
   var ids = [];
   columns.forEach(function(e) {
     ids.push(e.element.id);
   });
   is(compareArrays(ids, aReference), true, aMessage);
 }
 
 function mouseOnCell(tree, row, column, testname) {
-  var rect = tree.boxObject.getCoordsForCellItem(row, column, "text");
+  var rect = tree.getCoordsForCellItem(row, column, "text");
 
   synthesizeMouseExpectEvent(tree.body, rect.x, rect.y, {}, tree, "select", testname);
 }
 
 function mouseClickOnColumnHeader(aColumns, aColumnIndex, aButton, aClickCount) {
   var columnHeader = aColumns[aColumnIndex].element;
   var columnHeaderRect = columnHeader.getBoundingClientRect();
   var columnWidth = columnHeaderRect.right - columnHeaderRect.left;
@@ -1093,20 +1093,20 @@ function mouseClickOnColumnHeader(aColum
                       clickCount: i });
   }
 }
 
 function mouseDblClickOnCell(tree, row, column, testname) {
   // select the row we will edit
   var selection = tree.view.selection;
   selection.select(row);
-  tree.treeBoxObject.ensureRowIsVisible(row);
+  tree.ensureRowIsVisible(row);
 
   // get cell coordinates
-  var rect = tree.treeBoxObject.getCoordsForCellItem(row, column, "text");
+  var rect = tree.getCoordsForCellItem(row, column, "text");
 
   synthesizeMouse(tree.body, rect.x, rect.y, { clickCount: 2 });
 }
 
 function compareArrays(arr1, arr2) {
   if (arr1.length != arr2.length)
     return false;
 
--- a/toolkit/content/treeUtils.js
+++ b/toolkit/content/treeUtils.js
@@ -6,17 +6,17 @@
 
 var gTreeUtils = {
   deleteAll(aTree, aView, aItems, aDeletedItems) {
     for (var i = 0; i < aItems.length; ++i)
       aDeletedItems.push(aItems[i]);
     aItems.splice(0, aItems.length);
     var oldCount = aView.rowCount;
     aView._rowCount = 0;
-    aTree.treeBoxObject.rowCountChanged(0, -oldCount);
+    aTree.rowCountChanged(0, -oldCount);
   },
 
   deleteSelectedItems(aTree, aView, aItems, aDeletedItems) {
     var selection = aTree.view.selection;
     selection.selectEventsSuppressed = true;
 
     var rc = selection.getRangeCount();
     for (var i = 0; i < rc; ++i) {
@@ -32,23 +32,23 @@ var gTreeUtils = {
     for (i = 0; i < aItems.length; ++i) {
       if (!aItems[i]) {
         let j = i;
         while (j < aItems.length && !aItems[j])
           ++j;
         aItems.splice(i, j - i);
         nextSelection = j < aView.rowCount ? j - 1 : j - 2;
         aView._rowCount -= j - i;
-        aTree.treeBoxObject.rowCountChanged(i, i - j);
+        aTree.rowCountChanged(i, i - j);
       }
     }
 
     if (aItems.length) {
       selection.select(nextSelection);
-      aTree.treeBoxObject.ensureRowIsVisible(nextSelection);
+      aTree.ensureRowIsVisible(nextSelection);
       aTree.focus();
     }
     selection.selectEventsSuppressed = false;
   },
 
   sort(aTree, aView, aDataSet, aColumn, aComparator,
                  aLastSortColumn, aLastSortAscending) {
     var ascending = (aColumn == aLastSortColumn) ? !aLastSortAscending : true;
@@ -60,14 +60,14 @@ var gTreeUtils = {
       sortFunction = function(a, b) { return aComparator(a[aColumn], b[aColumn]); };
     }
     aDataSet.sort(sortFunction);
     if (!ascending)
       aDataSet.reverse();
 
     aTree.view.selection.clearSelection();
     aTree.view.selection.select(0);
-    aTree.treeBoxObject.invalidate();
-    aTree.treeBoxObject.ensureRowIsVisible(0);
+    aTree.invalidate();
+    aTree.ensureRowIsVisible(0);
 
     return ascending;
   },
 };
--- a/toolkit/content/widgets/tree.js
+++ b/toolkit/content/widgets/tree.js
@@ -17,17 +17,17 @@
        */
       this.addEventListener("mousedown", (event) => {
         if (this.parentNode.disabled)
           return;
         if (((!event.getModifierState("Accel") ||
               !this.parentNode.pageUpOrDownMovesSelection) &&
             !event.shiftKey && !event.metaKey) ||
           this.parentNode.view.selection.single) {
-          var b = this.parentNode.treeBoxObject;
+          var b = this.parentNode;
           var cell = b.getCellAt(event.clientX, event.clientY);
           var view = this.parentNode.view;
 
           // save off the last selected row
           this._lastSelectedRow = cell.row;
 
           if (cell.row == -1)
             return;
@@ -60,17 +60,17 @@
       /**
        * On a click (up+down on the same item), deselect everything
        * except this item.
        */
       this.addEventListener("click", (event) => {
         if (event.button != 0) { return; }
         if (this.parentNode.disabled)
           return;
-        var b = this.parentNode.treeBoxObject;
+        var b = this.parentNode;
         var cell = b.getCellAt(event.clientX, event.clientY);
         var view = this.parentNode.view;
 
         if (cell.row == -1)
           return;
 
         if (cell.childElt == "twisty") {
           if (view.selection.currentIndex >= 0 &&
@@ -121,24 +121,24 @@
       });
 
       /**
        * double-click
        */
       this.addEventListener("dblclick", (event) => {
         if (this.parentNode.disabled)
           return;
-        var tbo = this.parentNode.treeBoxObject;
+        var tree = this.parentNode;
         var view = this.parentNode.view;
         var row = view.selection.currentIndex;
 
         if (row == -1)
           return;
 
-        var cell = tbo.getCellAt(event.clientX, event.clientY);
+        var cell = tree.getCellAt(event.clientX, event.clientY);
 
         if (cell.childElt != "twisty") {
           this.parentNode.startEditing(row, cell.col);
         }
 
         if (this.parentNode._editingColumn || !view.isContainer(row))
           return;
 
@@ -311,42 +311,42 @@
       var tree = col.parentNode.parentNode;
       var sib;
       var column;
       if (col.mTargetCol) {
         // remove previous insertbefore/after attributes
         col.mTargetCol.removeAttribute("insertbefore");
         col.mTargetCol.removeAttribute("insertafter");
         column = tree.columns.getColumnFor(col.mTargetCol);
-        tree.treeBoxObject.invalidateColumn(column);
+        tree.invalidateColumn(column);
         sib = col.mTargetCol._previousVisibleColumn;
         if (sib) {
           sib.removeAttribute("insertafter");
           column = tree.columns.getColumnFor(sib);
-          tree.treeBoxObject.invalidateColumn(column);
+          tree.invalidateColumn(column);
         }
         col.mTargetCol = null;
         col.mTargetDir = null;
       }
 
       if (targetCol) {
         // set insertbefore/after attributes
         if (pos.value == "after") {
           targetCol.setAttribute("insertafter", "true");
         } else {
           targetCol.setAttribute("insertbefore", "true");
           sib = targetCol._previousVisibleColumn;
           if (sib) {
             sib.setAttribute("insertafter", "true");
             column = tree.columns.getColumnFor(sib);
-            tree.treeBoxObject.invalidateColumn(column);
+            tree.invalidateColumn(column);
           }
         }
         column = tree.columns.getColumnFor(targetCol);
-        tree.treeBoxObject.invalidateColumn(column);
+        tree.invalidateColumn(column);
         col.mTargetCol = targetCol;
         col.mTargetDir = pos.value;
       }
     }
 
     _onDragMouseUp(aEvent) {
       var col = document.treecolDragging;
       if (!col) return;
@@ -376,17 +376,17 @@
             move = false;
           }
 
           if (move) {
             col.parentNode.parentNode._reorderColumn(col, col.mTargetCol, before);
           }
 
           // repaint to remove lines
-          col.parentNode.parentNode.treeBoxObject.invalidate();
+          col.parentNode.parentNode.invalidate();
 
           col.mTargetCol = null;
         }
       } else
         col.mDragGesturing = false;
 
       document.treecolDragging = null;
       col.removeAttribute("dragging");
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -33,25 +33,18 @@
           oncontextmenu="event.stopPropagation(); event.preventDefault();"
           onclick="event.stopPropagation(); event.preventDefault();"
           ondblclick="event.stopPropagation();"
           oncommand="event.stopPropagation();"/>
       </xul:hbox>
     </content>
 
     <implementation implements="nsIDOMXULMultiSelectControlElement">
-      <property name="columns"
-                onget="return this.treeBoxObject.columns;"/>
-
-      <property name="view"
-                onget="return this.treeBoxObject.view"
-                onset="return this.treeBoxObject.view = val;"/>
-
       <property name="body"
-                onget="return this.treeBoxObject.treeBody;"/>
+                onget="return this.treeBody;"/>
 
       <property name="editable"
                 onget="return this.getAttribute('editable') == 'true';"
                 onset="if (val) this.setAttribute('editable', 'true');
                        else this.removeAttribute('editable'); return val;"/>
 
       <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
 
@@ -60,20 +53,16 @@
       <property name="selType"
                 onget="return this.getAttribute('seltype')"
                 onset="this.setAttribute('seltype', val); return val;"/>
 
       <property name="currentIndex"
                 onget="return this.view ? this.view.selection.currentIndex: - 1;"
                 onset="if (this.view) return this.view.selection.currentIndex = val; return val;"/>
 
-      <property name="treeBoxObject"
-                onget="return this.boxObject;"
-                readonly="true"/>
-
       <field name="pageUpOrDownMovesSelection">
         !/Mac/.test(navigator.platform)
       </field>
       <property name="keepCurrentInView"
                 onget="return (this.getAttribute('keepcurrentinview') == 'true');"
                 onset="if (val) this.setAttribute('keepcurrentinview', 'true');
                        else this.removeAttribute('keepcurrentinview'); return val;"/>
 
@@ -188,17 +177,17 @@
           var col = this.columns.getFirstColumn();
           while (col) {
             columns.push(col);
             col = col.getNext();
           }
           if (isRTL)
             columns.reverse();
           var currentX = this.boxObject.x;
-          var adjustedX = aX + this.treeBoxObject.horizontalPosition;
+          var adjustedX = aX + this.horizontalPosition;
           for (var i = 0; i < columns.length; ++i) {
             col = columns[i];
             var cw = col.element.boxObject.width;
             if (cw > 0) {
               currentX += cw;
               if (currentX - (cw * aThresh) > adjustedX)
                 return col.element;
             }
@@ -294,24 +283,23 @@
               return false;
 
             // Beyond this point, we are going to edit the cell.
             if (this._editingColumn)
               this.stopEditing();
 
             var input = this.inputField;
 
-            var box = this.treeBoxObject;
-            box.ensureCellIsVisible(row, column);
+            this.ensureCellIsVisible(row, column);
 
             // Get the coordinates of the text inside the cell.
-            var textRect = box.getCoordsForCellItem(row, column, "text");
+            var textRect = this.getCoordsForCellItem(row, column, "text");
 
             // Get the coordinates of the cell itself.
-            var cellRect = box.getCoordsForCellItem(row, column, "cell");
+            var cellRect = this.getCoordsForCellItem(row, column, "cell");
 
             // Calculate the top offset of the textbox.
             var style = window.getComputedStyle(input);
             var topadj = parseInt(style.borderTopWidth) + parseInt(style.paddingTop);
             input.top = textRect.y - topadj;
 
             // The leftside of the textbox is aligned to the left side of the text
             // in LTR mode, and left side of the cell in RTL mode.
@@ -335,17 +323,17 @@
 
             input.select();
             input.inputField.focus();
 
             this._editingRow = row;
             this._editingColumn = column;
             this.setAttribute("editing", "true");
 
-            box.invalidateCell(row, column);
+            this.invalidateCell(row, column);
             return true;
           ]]>
         </body>
       </method>
 
       <method name="stopEditing">
         <parameter name="accept"/>
         <body>
@@ -377,49 +365,49 @@
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
             if (event.getModifierState("Accel") && this.view.selection.single) {
-              this.treeBoxObject.scrollByLines(offset);
+              this.scrollByLines(offset);
               return;
             }
 
             var c = this.currentIndex + offset;
             if (offset > 0 ? c > edge : c < edge) {
               if (this.view.selection.isSelected(edge) && this.view.selection.count <= 1)
                 return;
               c = edge;
             }
 
             if (!event.getModifierState("Accel"))
               this.view.selection.timedSelect(c, this._selectDelay);
             else // Ctrl+Up/Down moves the anchor without selecting
               this.currentIndex = c;
-            this.treeBoxObject.ensureRowIsVisible(c);
+            this.ensureRowIsVisible(c);
           ]]>
         </body>
       </method>
 
       <method name="_moveByOffsetShift">
         <parameter name="offset"/>
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.selection.single) {
-              this.treeBoxObject.scrollByLines(offset);
+              this.scrollByLines(offset);
               return;
             }
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
 
@@ -430,17 +418,17 @@
             if (c == edge) {
               if (this.view.selection.isSelected(c))
                 return;
             }
 
             // Extend the selection from the existing pivot, if any
             this.view.selection.rangedSelect(-1, c + offset,
                                              event.getModifierState("Accel"));
-            this.treeBoxObject.ensureRowIsVisible(c + offset);
+            this.ensureRowIsVisible(c + offset);
 
           ]]>
         </body>
       </method>
 
       <method name="_moveByPage">
         <parameter name="offset"/>
         <parameter name="edge"/>
@@ -448,47 +436,47 @@
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
             if (this.pageUpOrDownMovesSelection == event.getModifierState("Accel")) {
-               this.treeBoxObject.scrollByPages(offset);
+               this.scrollByPages(offset);
                return;
             }
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
 
             var c = this.currentIndex;
             if (c == -1)
               return;
 
             if (c == edge && this.view.selection.isSelected(c)) {
-              this.treeBoxObject.ensureRowIsVisible(c);
+              this.ensureRowIsVisible(c);
               return;
             }
-            var i = this.treeBoxObject.getFirstVisibleRow();
-            var p = this.treeBoxObject.getPageLength();
+            var i = this.getFirstVisibleRow();
+            var p = this.getPageLength();
 
             if (offset > 0) {
               i += p - 1;
               if (c >= i) {
                  i = c + p;
-                 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
+                 this.ensureRowIsVisible(i > edge ? edge : i);
               }
               i = i > edge ? edge : i;
 
             } else if (c <= i) {
                i = c <= p ? 0 : c - p;
-               this.treeBoxObject.ensureRowIsVisible(i);
+               this.ensureRowIsVisible(i);
             }
             this.view.selection.timedSelect(i, this._selectDelay);
           ]]>
         </body>
       </method>
 
       <method name="_moveByPageShift">
         <parameter name="offset"/>
@@ -509,36 +497,36 @@
 
             if (this.view.selection.single)
               return;
 
             var c = this.currentIndex;
             if (c == -1)
               return;
             if (c == edge && this.view.selection.isSelected(c)) {
-              this.treeBoxObject.ensureRowIsVisible(edge);
+              this.ensureRowIsVisible(edge);
               return;
             }
-            var i = this.treeBoxObject.getFirstVisibleRow();
-            var p = this.treeBoxObject.getPageLength();
+            var i = this.getFirstVisibleRow();
+            var p = this.getPageLength();
 
             if (offset > 0) {
               i += p - 1;
               if (c >= i) {
                  i = c + p;
-                 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
+                 this.ensureRowIsVisible(i > edge ? edge : i);
               }
               // Extend the selection from the existing pivot, if any
               this.view.selection.rangedSelect(-1, i > edge ? edge : i, event.getModifierState("Accel"));
 
             } else {
 
               if (c <= i) {
                  i = c <= p ? 0 : c - p;
-                 this.treeBoxObject.ensureRowIsVisible(i);
+                 this.ensureRowIsVisible(i);
               }
               // Extend the selection from the existing pivot, if any
               this.view.selection.rangedSelect(-1, i, event.getModifierState("Accel"));
             }
 
           ]]>
         </body>
       </method>
@@ -561,17 +549,17 @@
             // Normal behaviour is to select the first/last row
             if (!event.getModifierState("Accel"))
               this.view.selection.timedSelect(edge, this._selectDelay);
 
             // In a multiselect tree Ctrl+Home/End moves the anchor
             else if (!this.view.selection.single)
               this.currentIndex = edge;
 
-            this.treeBoxObject.ensureRowIsVisible(edge);
+            this.ensureRowIsVisible(edge);
           ]]>
         </body>
       </method>
 
       <method name="_moveToEdgeShift">
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
@@ -589,17 +577,17 @@
             if (this.view.selection.single ||
                 (this.view.selection.isSelected(edge)) && this.view.selection.isSelected(this.currentIndex))
               return;
 
             // Extend the selection from the existing pivot, if any.
             // -1 doesn't work here, so using currentIndex instead
             this.view.selection.rangedSelect(this.currentIndex, edge, event.getModifierState("Accel"));
 
-            this.treeBoxObject.ensureRowIsVisible(edge);
+            this.ensureRowIsVisible(edge);
           ]]>
         </body>
       </method>
       <method name="_handleEnter">
         <parameter name="event"/>
         <body><![CDATA[
           if (this._editingColumn) {
             this.stopEditing(true);
@@ -634,20 +622,20 @@
           }
         ]]>
       </handler>
       <handler event="touchmove">
         <![CDATA[
           if (event.touches.length == 1 &&
               this._touchY >= 0) {
             var deltaY = this._touchY - event.touches[0].screenY;
-            var lines = Math.trunc(deltaY / this.treeBoxObject.rowHeight);
+            var lines = Math.trunc(deltaY / this.rowHeight);
             if (Math.abs(lines) > 0) {
-              this.treeBoxObject.scrollByLines(lines);
-              deltaY -= lines * this.treeBoxObject.rowHeight;
+              this.scrollByLines(lines);
+              deltaY -= lines * this.rowHeight;
               this._touchY = event.touches[0].screenY + deltaY;
             }
             event.preventDefault();
           }
         ]]>
       </handler>
       <handler event="touchend">
         <![CDATA[
@@ -669,50 +657,50 @@
 
           if (this._editingColumn)
             return;
           if (event.axis == event.HORIZONTAL_AXIS)
             return;
 
           var rows = event.detail;
           if (rows == UIEvent.SCROLL_PAGE_UP)
-            this.treeBoxObject.scrollByPages(-1);
+            this.scrollByPages(-1);
           else if (rows == UIEvent.SCROLL_PAGE_DOWN)
-            this.treeBoxObject.scrollByPages(1);
+            this.scrollByPages(1);
           else
-            this.treeBoxObject.scrollByLines(rows);
+            this.scrollByLines(rows);
         ]]>
       </handler>
       <handler event="MozSwipeGesture" preventdefault="true">
         <![CDATA[
           // Figure out which row to show
           let targetRow = 0;
 
           // Only handle swipe gestures up and down
           switch (event.direction) {
             case event.DIRECTION_DOWN:
               targetRow = this.view.rowCount - 1;
               // Fall through for actual action
             case event.DIRECTION_UP:
-              this.treeBoxObject.ensureRowIsVisible(targetRow);
+              this.ensureRowIsVisible(targetRow);
               break;
           }
         ]]>
       </handler>
       <handler event="select" phase="target"
                action="if (event.originalTarget == this) this.stopEditing(true);"/>
       <handler event="focus">
         <![CDATA[
-          this.treeBoxObject.focused = true;
+          this.focused = true;
           if (this.currentIndex == -1 && this.view.rowCount > 0) {
-            this.currentIndex = this.treeBoxObject.getFirstVisibleRow();
+            this.currentIndex = this.getFirstVisibleRow();
           }
         ]]>
       </handler>
-      <handler event="blur" action="this.treeBoxObject.focused = false;"/>
+      <handler event="blur" action="this.focused = false;"/>
       <handler event="blur" phase="capturing"
                action="if (event.originalTarget == this.inputField.inputField) this.stopEditing(true);"/>
       <handler event="keydown" keycode="VK_RETURN">
         if (this._handleEnter(event)) {
           event.stopPropagation();
           event.preventDefault();
         }
       </handler>
@@ -737,17 +725,17 @@
 
          if (this.changeOpenState(this.currentIndex, false)) {
            event.preventDefault();
            return;
          }
          var parentIndex = this.view.getParentIndex(this.currentIndex);
          if (parentIndex >= 0) {
            this.view.selection.select(parentIndex);
-           this.treeBoxObject.ensureRowIsVisible(parentIndex);
+           this.ensureRowIsVisible(parentIndex);
            event.preventDefault();
          }
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_RIGHT">
         <![CDATA[
          if (this._editingColumn)
            return;
@@ -763,17 +751,17 @@
           var c = row + 1;
           var view = this.view;
           if (c < view.rowCount &&
               view.getParentIndex(c) == row) {
             // If already opened, select the first child.
             // The getParentIndex test above ensures that the children
             // are already populated and ready.
             this.view.selection.timedSelect(c, this._selectDelay);
-            this.treeBoxObject.ensureRowIsVisible(c);
+            this.ensureRowIsVisible(c);
             event.preventDefault();
           }
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_UP" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
@@ -870,17 +858,17 @@
              event.preventDefault();
            }
          } else if (!this.disableKeyNavigation && event.charCode > 0 &&
                     !event.altKey && !event.getModifierState("Accel") &&
                     !event.metaKey && !event.ctrlKey) {
            var l = this._keyNavigate(event);
            if (l >= 0) {
              this.view.selection.timedSelect(l, this._selectDelay);
-             this.treeBoxObject.ensureRowIsVisible(l);
+             this.ensureRowIsVisible(l);
            }
            event.preventDefault();
          }
          ]]>
       </handler>
     </handlers>
   </binding>