Bug 1465219, use XULMenuElement, a subclass of nsXULElement, instead of MenuBoxObject for menu and menulist elements, r=paolo,bz
authorEmma Malysz <emalysz@mozilla.com>
Tue, 31 Jul 2018 12:30:17 -0700
changeset 492966 6dd5d0b6ba70f12bef8b14d1bff19d15f3f3aac7
parent 492965 343c194c1a081804cabe4560f16ddfe004caa097
child 492967 7e69cb7b920612ef9c4d88ea48d5bbdf47970720
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaolo, bz
bugs1465219
milestone64.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 1465219, use XULMenuElement, a subclass of nsXULElement, instead of MenuBoxObject for menu and menulist elements, r=paolo,bz MozReview-Commit-ID: 5253hAlxbhw
accessible/tests/mochitest/events.js
browser/base/content/test/forms/browser_selectpopup.js
browser/base/content/test/general/browser_bug647886.js
browser/components/uitour/UITour.jsm
dom/base/nsDocument.cpp
dom/bindings/BindingUtils.cpp
dom/chrome-webidl/XULMenuElement.webidl
dom/chrome-webidl/moz.build
dom/tests/mochitest/general/test_interfaces.js
dom/webidl/MenuBoxObject.webidl
dom/webidl/XULElement.webidl
dom/webidl/moz.build
dom/xul/XULMenuElement.cpp
dom/xul/XULMenuElement.h
dom/xul/moz.build
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
layout/xul/MenuBoxObject.cpp
layout/xul/MenuBoxObject.h
layout/xul/moz.build
layout/xul/nsDeckFrame.cpp
layout/xul/nsProgressMeterFrame.cpp
toolkit/content/tests/chrome/popup_trigger.js
toolkit/content/tests/chrome/test_contextmenu_list.xul
toolkit/content/tests/chrome/test_menulist.xul
toolkit/content/tests/chrome/test_menulist_keynav.xul
toolkit/content/tests/chrome/test_menulist_paging.xul
toolkit/content/tests/widgets/popup_shared.js
toolkit/content/widgets/button.xml
toolkit/content/widgets/menu.xml
toolkit/content/widgets/menulist.xml
toolkit/modules/SelectParentHelper.jsm
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -65,16 +65,21 @@ var gA11yEventDumpFeature = "";
 /**
  * Function to detect HTML elements when given a node.
  */
 function isHTMLElement(aNode) {
   return aNode.nodeType == aNode.ELEMENT_NODE &&
          aNode.namespaceURI == "http://www.w3.org/1999/xhtml";
 }
 
+function isXULElement(aNode) {
+  return aNode.nodeType == aNode.ELEMENT_NODE &&
+         aNode.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+}
+
 /**
  * Executes the function when requested event is handled.
  *
  * @param aEventType  [in] event type
  * @param aTarget     [in] event target
  * @param aFunc       [in] function to call when event is handled
  * @param aContext    [in, optional] object in which context the function is
  *                    called
@@ -1087,27 +1092,28 @@ function synthClick(aNodeOrID, aCheckerO
     if (targetNode.nodeType == targetNode.DOCUMENT_NODE) {
       targetNode =
         this.DOMNode.body ? this.DOMNode.body : this.DOMNode.documentElement;
     }
 
     // Scroll the node into view, otherwise synth click may fail.
     if (isHTMLElement(targetNode)) {
       targetNode.scrollIntoView(true);
-    } else if (ChromeUtils.getClassName(targetNode) == "XULElement") {
+    } else if (isXULElement(targetNode)) {
       var targetAcc = getAccessible(targetNode);
       targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE);
     }
 
     var x = 1, y = 1;
     if (aArgs && ("where" in aArgs) && aArgs.where == "right") {
-      if (isHTMLElement(targetNode))
+      if (isHTMLElement(targetNode)) {
         x = targetNode.offsetWidth - 1;
-    else if (ChromeUtils.getClassName(targetNode) == "XULElement")
+      } else if (isXULElement(targetNode)) {
         x = targetNode.boxObject.width - 1;
+      }
     }
     synthesizeMouse(targetNode, x, y, aArgs ? aArgs : {});
   };
 
   this.finalCheck = function synthClick_finalCheck() {
     // Scroll top window back.
     window.top.scrollTo(0, 0);
   };
--- a/browser/base/content/test/forms/browser_selectpopup.js
+++ b/browser/base/content/test/forms/browser_selectpopup.js
@@ -140,36 +140,36 @@ async function doSelectTests(contentType
 
   is(menulist.selectedIndex, 1, "Initial selection");
   is(selectPopup.firstElementChild.localName, "menucaption", "optgroup is caption");
   is(selectPopup.firstElementChild.getAttribute("label"), "First Group", "optgroup label");
   is(selectPopup.children[1].localName, "menuitem", "option is menuitem");
   is(selectPopup.children[1].getAttribute("label"), "One", "option label");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-  is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(2), "Select item 2");
+  is(menulist.activeChild, menulist.getItemAtIndex(2), "Select item 2");
   is(menulist.selectedIndex, isWindows ? 2 : 1, "Select item 2 selectedIndex");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-  is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3");
+  is(menulist.activeChild, menulist.getItemAtIndex(3), "Select item 3");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
 
   // On Windows, one can navigate on disabled menuitems
-  is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(9),
+  is(menulist.activeChild, menulist.getItemAtIndex(9),
      "Skip optgroup header and disabled items select item 7");
   is(menulist.selectedIndex, isWindows ? 9 : 1, "Select or skip disabled item selectedIndex");
 
   for (let i = 0; i < 10; i++) {
     is(menulist.getItemAtIndex(i).disabled, i >= 4 && i <= 7, "item " + i + " disabled");
   }
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-  is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
+  is(menulist.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
   is((await getInputEvents()), 0, "Before closed - number of input events");
   is((await getChangeEvents()), 0, "Before closed - number of change events");
   is((await getClickEvents()), 0, "Before closed - number of click events");
 
   EventUtils.synthesizeKey("a", { accelKey: true });
   await ContentTask.spawn(gBrowser.selectedBrowser, { isWindows }, function(args) {
--- a/browser/base/content/test/general/browser_bug647886.js
+++ b/browser/base/content/test/general/browser_bug647886.js
@@ -3,18 +3,20 @@
 
 add_task(async function() {
   await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
 
   await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
     content.history.pushState({}, "2", "2.html");
   });
 
-  var backButton = document.getElementById("back-button");
-  var rect = backButton.getBoundingClientRect();
+  await new Promise(resolve => SessionStore.getSessionHistory(gBrowser.selectedTab, resolve));
+
+  let backButton = document.getElementById("back-button");
+  let rect = backButton.getBoundingClientRect();
 
   info("waiting for the history menu to open");
 
   let popupShownPromise = BrowserTestUtils.waitForEvent(backButton, "popupshown");
   EventUtils.synthesizeMouseAtCenter(backButton, {type: "mousedown"});
   EventUtils.synthesizeMouse(backButton, rect.width / 2, rect.height, {type: "mouseup"});
   let event = await popupShownPromise;
 
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1241,24 +1241,24 @@ var UITour = {
     this._hideInfoElement(aWindow);
     this._setMenuStateForAnnotation(aWindow, false, "appMenu");
     this._setMenuStateForAnnotation(aWindow, false, "pageActionPanel");
   },
 
   showMenu(aWindow, aMenuName, aOpenCallback = null) {
     log.debug("showMenu:", aMenuName);
     function openMenuButton(aMenuBtn) {
-      if (!aMenuBtn || !aMenuBtn.boxObject || aMenuBtn.open) {
+      if (!aMenuBtn || !aMenuBtn.hasMenu() || aMenuBtn.open) {
         if (aOpenCallback)
           aOpenCallback();
         return;
       }
       if (aOpenCallback)
         aMenuBtn.addEventListener("popupshown", aOpenCallback, { once: true });
-      aMenuBtn.boxObject.openMenu(true);
+      aMenuBtn.openMenu(true);
     }
 
     if (aMenuName == "appMenu" || aMenuName == "pageActionPanel") {
       let menu = {
         onPanelHidden: this.onPanelHidden,
       };
       if (aMenuName == "appMenu") {
         menu.node = aWindow.PanelUI.panel;
@@ -1337,18 +1337,19 @@ var UITour = {
         urlbar.controller.startSearch(SEARCH_STRING);
       }).catch(Cu.reportError);
     }
   },
 
   hideMenu(aWindow, aMenuName) {
     log.debug("hideMenu:", aMenuName);
     function closeMenuButton(aMenuBtn) {
-      if (aMenuBtn && aMenuBtn.boxObject)
-        aMenuBtn.boxObject.openMenu(false);
+      if (aMenuBtn && aMenuBtn.hasMenu()) {
+        aMenuBtn.openMenu(false);
+      }
     }
 
     if (aMenuName == "appMenu") {
       aWindow.PanelUI.hide();
     } else if (aMenuName == "bookmarks") {
       let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
       closeMenuButton(menuBtn);
     } else if (aMenuName == "controlCenter") {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -254,17 +254,16 @@
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleSheet.h"
 #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/MenuBoxObject.h"
 #include "mozilla/dom/TreeBoxObject.h"
 #include "nsIXULWindow.h"
 #include "nsXULCommandDispatcher.h"
 #include "nsXULPopupManager.h"
 #include "nsIDocShellTreeOwner.h"
 #endif
 #include "nsIPresShellInlines.h"
 
@@ -6541,19 +6540,17 @@ nsIDocument::GetBoxObjectFor(Element* aE
     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::menu) {
-      boxObject = new MenuBoxObject();
-    } else if (tag == nsGkAtoms::tree) {
+    if (tag == nsGkAtoms::tree) {
       boxObject = new TreeBoxObject();
     } else {
       boxObject = new BoxObject();
     }
   } else
 #endif // MOZ_XUL
   {
     boxObject = new BoxObject();
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -47,16 +47,17 @@
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElement.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/XULFrameElementBinding.h"
+#include "mozilla/dom/XULMenuElementBinding.h"
 #include "mozilla/dom/XULPopupElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
 #include "mozilla/dom/XULScrollElementBinding.h"
@@ -3856,16 +3857,19 @@ HTMLConstructor(JSContext* aCx, unsigned
         definition->mLocalName == nsGkAtoms::popup ||
         definition->mLocalName == nsGkAtoms::panel ||
         definition->mLocalName == nsGkAtoms::tooltip) {
       cb = XULPopupElement_Binding::GetConstructorObject;
     } else if (definition->mLocalName == nsGkAtoms::iframe ||
                 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 {
       cb = XULElement_Binding::GetConstructorObject;
     }
   }
 
   int32_t tag = eHTMLTag_userdefined;
rename from dom/webidl/MenuBoxObject.webidl
rename to dom/chrome-webidl/XULMenuElement.webidl
--- a/dom/webidl/MenuBoxObject.webidl
+++ b/dom/chrome-webidl/XULMenuElement.webidl
@@ -1,19 +1,17 @@
 
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Func="IsChromeOrXBL"]
-interface MenuBoxObject : BoxObject {
-
-  void openMenu(boolean openFlag);
+[HTMLConstructor, Func="IsChromeOrXBL"]
+interface XULMenuElement : XULElement {
 
   attribute Element? activeChild;
 
   boolean handleKeyPress(KeyboardEvent keyEvent);
 
   readonly attribute boolean openedWithKey;
 
 };
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -45,16 +45,17 @@ WEBIDL_FILES = [
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
     'PrecompiledScript.webidl',
     'PromiseDebugging.webidl',
     'StructuredCloneHolder.webidl',
     'WebExtensionContentScript.webidl',
     'WebExtensionPolicy.webidl',
     'XULFrameElement.webidl',
+    'XULMenuElement.webidl',
     'XULScrollElement.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
@@ -661,18 +661,16 @@ var interfaceNamesInGlobalScope =
     {name: "MediaStreamAudioSourceNode", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaStreamEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaStreamTrackEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaStreamTrack", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MenuBoxObject", insecureContext: true, xbl: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessageChannel", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessageEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessagePort", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MIDIAccess", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -1257,16 +1255,18 @@ var interfaceNamesInGlobalScope =
     {name: "XULCommandEvent", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULDocument", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "XULFrameElement", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {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!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
 function createInterfaceMap(isXBLScope) {
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -80,13 +80,21 @@ interface XULElement : Element {
   [Throws]
   void                      blur();
   [NeedsCallerType]
   void                      click();
   void                      doCommand();
 
   [Constant]
   readonly attribute CSSStyleDeclaration style;
+
+  // Returns true if this is a menu-type element that has a menu
+  // frame associated with it.
+  boolean hasMenu();
+
+  // If this is a menu-type element, opens or closes the menu
+  // depending on the argument passed.
+  void openMenu(boolean open);
 };
 
 XULElement implements GlobalEventHandlers;
 XULElement implements TouchEventHandlers;
 XULElement implements OnErrorEventHandlerForNodes;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -660,17 +660,16 @@ WEBIDL_FILES = [
     'MediaStream.webidl',
     'MediaStreamAudioDestinationNode.webidl',
     'MediaStreamAudioSourceNode.webidl',
     'MediaStreamError.webidl',
     'MediaStreamTrack.webidl',
     'MediaTrackConstraintSet.webidl',
     'MediaTrackSettings.webidl',
     'MediaTrackSupportedConstraints.webidl',
-    'MenuBoxObject.webidl',
     'MerchantValidationEvent.webidl',
     'MessageChannel.webidl',
     'MessageEvent.webidl',
     'MessagePort.webidl',
     'MIDIAccess.webidl',
     'MIDIInput.webidl',
     'MIDIInputMap.webidl',
     'MIDIMessageEvent.webidl',
rename from layout/xul/MenuBoxObject.cpp
rename to dom/xul/XULMenuElement.cpp
--- a/layout/xul/MenuBoxObject.cpp
+++ b/dom/xul/XULMenuElement.cpp
@@ -1,100 +1,86 @@
 /* -*- 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/MenuBoxObject.h"
-#include "mozilla/dom/MenuBoxObjectBinding.h"
-
 #include "mozilla/dom/KeyboardEvent.h"
 #include "mozilla/dom/KeyboardEventBinding.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
 #include "nsMenuBarFrame.h"
 #include "nsMenuBarListener.h"
 #include "nsMenuFrame.h"
 #include "nsMenuPopupFrame.h"
+#include "mozilla/dom/XULMenuElement.h"
+#include "mozilla/dom/XULMenuElementBinding.h"
+#include "nsXULPopupManager.h"
 
 namespace mozilla {
 namespace dom {
 
-MenuBoxObject::MenuBoxObject()
-{
-}
-
-MenuBoxObject::~MenuBoxObject()
+JSObject*
+XULMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
-}
-
-JSObject* MenuBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return MenuBoxObject_Binding::Wrap(aCx, this, aGivenProto);
+  return XULMenuElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-void MenuBoxObject::OpenMenu(bool aOpenFlag)
+nsIFrame*
+XULMenuElement::GetFrame()
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm) {
-    nsIFrame* frame = GetFrame(false);
-    if (frame) {
-      if (aOpenFlag) {
-        nsCOMPtr<nsIContent> content = mContent;
-        pm->ShowMenu(content, false, false);
-      }
-      else {
-        nsMenuFrame* menu = do_QueryFrame(frame);
-        if (menu) {
-          nsMenuPopupFrame* popupFrame = menu->GetPopup();
-          if (popupFrame)
-            pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
-        }
-      }
-    }
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
+
+  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(FlushType::Frames);
   }
+
+  return GetPrimaryFrame();
 }
 
 already_AddRefed<Element>
-MenuBoxObject::GetActiveChild()
+XULMenuElement::GetActiveChild()
 {
-  nsMenuFrame* menu = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menu = do_QueryFrame(GetFrame());
   if (menu) {
     RefPtr<Element> el;
     menu->GetActiveChild(getter_AddRefs(el));
     return el.forget();
   }
   return nullptr;
 }
 
-void MenuBoxObject::SetActiveChild(Element* arg)
+void
+XULMenuElement::SetActiveChild(Element* arg)
 {
-  nsMenuFrame* menu = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menu = do_QueryFrame(GetFrame());
   if (menu) {
     menu->SetActiveChild(arg);
   }
 }
 
-bool MenuBoxObject::HandleKeyPress(KeyboardEvent& keyEvent)
+bool
+XULMenuElement::HandleKeyPress(KeyboardEvent& keyEvent)
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (!pm) {
     return false;
   }
 
   // if event has already been handled, bail
   if (keyEvent.DefaultPrevented()) {
     return false;
   }
 
   if (nsMenuBarListener::IsAccessKeyPressed(&keyEvent))
     return false;
 
-  nsMenuFrame* menu = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menu = do_QueryFrame(GetFrame());
   if (!menu) {
     return false;
   }
 
   nsMenuPopupFrame* popupFrame = menu->GetPopup();
   if (!popupFrame) {
     return false;
   }
@@ -110,19 +96,20 @@ bool MenuBoxObject::HandleKeyPress(Keybo
       theDirection = NS_DIRECTION_FROM_KEY_CODE(popupFrame, keyCode);
       return pm->HandleKeyboardNavigationInPopup(popupFrame, theDirection);
     }
     default:
       return pm->HandleShortcutNavigation(&keyEvent, popupFrame);
   }
 }
 
-bool MenuBoxObject::OpenedWithKey()
+bool
+XULMenuElement::OpenedWithKey()
 {
-  nsMenuFrame* menuframe = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menuframe = do_QueryFrame(GetFrame());
   if (!menuframe) {
     return false;
   }
 
   nsIFrame* frame = menuframe->GetParent();
   while (frame) {
     nsMenuBarFrame* menubar = do_QueryFrame(frame);
     if (menubar) {
rename from layout/xul/MenuBoxObject.h
rename to dom/xul/XULMenuElement.h
--- a/layout/xul/MenuBoxObject.h
+++ b/dom/xul/XULMenuElement.h
@@ -1,38 +1,41 @@
 /* -*- 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_MenuBoxObject_h
-#define mozilla_dom_MenuBoxObject_h
+#ifndef mozilla_dom_XULMenuElement_h
+#define mozilla_dom_XULMenuElement_h
 
-#include "mozilla/dom/BoxObject.h"
+#include "nsXULElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class KeyboardEvent;
 
-class MenuBoxObject final : public BoxObject
+class XULMenuElement final : public nsXULElement
 {
 public:
 
-  MenuBoxObject();
-
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  explicit XULMenuElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+    : nsXULElement(aNodeInfo)
+  {
+  }
 
-  void OpenMenu(bool aOpenFlag);
-  already_AddRefed<Element> GetActiveChild();
-  void SetActiveChild(Element* arg);
-  bool HandleKeyPress(KeyboardEvent& keyEvent);
-  bool OpenedWithKey();
+  MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> GetActiveChild();
+  MOZ_CAN_RUN_SCRIPT void SetActiveChild(Element* arg);
+  MOZ_CAN_RUN_SCRIPT bool HandleKeyPress(KeyboardEvent& keyEvent);
+  MOZ_CAN_RUN_SCRIPT bool OpenedWithKey();
 
 private:
-  ~MenuBoxObject();
+  virtual ~XULMenuElement() {}
+  JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+  nsIFrame* GetFrame();
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_MenuBoxObject_h
+#endif // XULMenuElement_h
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -18,31 +18,33 @@ if CONFIG['MOZ_XUL']:
     EXPORTS += [
         'nsXULCommandDispatcher.h',
         'nsXULElement.h',
         'nsXULSortService.h',
     ]
 
     EXPORTS.mozilla.dom += [
         'XULFrameElement.h',
+        'XULMenuElement.h',
         'XULPopupElement.h',
         'XULScrollElement.h',
     ]
 
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULContentUtils.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
         'nsXULPrototypeCache.cpp',
         'nsXULPrototypeDocument.cpp',
         'nsXULSortService.cpp',
         'XULDocument.cpp',
         'XULFrameElement.cpp',
+        'XULMenuElement.cpp',
         'XULPopupElement.cpp',
         'XULScrollElement.cpp',
     ]
 
 XPIDL_SOURCES += [
     'nsIController.idl',
     'nsIControllers.idl',
 ]
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -70,16 +70,17 @@
 #include "nsXBLBinding.h"
 #include "nsXULTooltipListener.h"
 #include "mozilla/EventDispatcher.h"
 #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 "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/BoxObject.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/dom/XULCommandEvent.h"
@@ -154,16 +155,22 @@ nsXULElement* nsXULElement::Construct(al
 
   if (nodeInfo->Equals(nsGkAtoms::iframe) ||
       nodeInfo->Equals(nsGkAtoms::browser) ||
       nodeInfo->Equals(nsGkAtoms::editor)) {
     already_AddRefed<mozilla::dom::NodeInfo> frameni = nodeInfo.forget();
     return new XULFrameElement(frameni);
   }
 
+  if (nodeInfo->Equals(nsGkAtoms::menu) ||
+      nodeInfo->Equals(nsGkAtoms::menulist)) {
+    already_AddRefed<mozilla::dom::NodeInfo> menuni = nodeInfo.forget();
+    return new XULMenuElement(menuni);
+  }
+
   if (nodeInfo->Equals(nsGkAtoms::scrollbox)) {
     already_AddRefed<mozilla::dom::NodeInfo> scrollni = nodeInfo.forget();
     return new XULScrollElement(scrollni);
   }
 
   return NS_NewBasicXULElement(nodeInfo.forget());
 }
 
@@ -492,16 +499,49 @@ nsXULElement::IsFocusableInternal(int32_
       shouldFocus = *aTabIndex >= 0;
     }
   }
 
   return shouldFocus;
 }
 
 bool
+nsXULElement::HasMenu()
+{
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
+  return menu != nullptr;
+}
+
+void
+nsXULElement::OpenMenu(bool aOpenFlag)
+{
+  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(FlushType::Frames);
+  }
+
+  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+  if (pm) {
+    if (aOpenFlag) {
+      // Nothing will happen if this element isn't a menu.
+      pm->ShowMenu(this, false, false);
+    }
+    else {
+      nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
+      if (menu) {
+        nsMenuPopupFrame* popupFrame = menu->GetPopup();
+        if (popupFrame) {
+          pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
+        }
+      }
+    }
+  }
+}
+
+bool
 nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
                                bool aIsTrustedEvent)
 {
     RefPtr<Element> content(this);
 
     if (IsXULElement(nsGkAtoms::label)) {
         nsAutoString control;
         GetAttr(kNameSpaceID_None, nsGkAtoms::control, control);
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -370,16 +370,19 @@ public:
 
 #ifdef DEBUG
     virtual void List(FILE* out, int32_t aIndent) const override;
     virtual void DumpContent(FILE* out, int32_t aIndent,bool aDumpAll) const override
     {
     }
 #endif
 
+    bool HasMenu();
+    MOZ_CAN_RUN_SCRIPT void OpenMenu(bool aOpenFlag);
+
     virtual bool PerformAccesskey(bool aKeyCausesActivation,
                                   bool aIsTrustedEvent) override;
     void ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent);
 
     nsIContent* GetBindingParent() const final
     {
       return mBindingParent;
     }
--- a/layout/xul/moz.build
+++ b/layout/xul/moz.build
@@ -26,17 +26,16 @@ EXPORTS += [
     'nsIScrollbarMediator.h',
     'nsPIBoxObject.h',
     'nsXULPopupManager.h',
     'nsXULTooltipListener.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BoxObject.h',
-    'MenuBoxObject.h',
 ]
 
 UNIFIED_SOURCES += [
     'BoxObject.cpp',
     'nsBox.cpp',
     'nsBoxFrame.cpp',
     'nsBoxLayout.cpp',
     'nsBoxLayoutState.cpp',
@@ -49,17 +48,16 @@ UNIFIED_SOURCES += [
     'nsSprocketLayout.cpp',
     'nsStackFrame.cpp',
     'nsStackLayout.cpp',
     'nsXULTooltipListener.cpp',
 ]
 
 if CONFIG['MOZ_XUL']:
     UNIFIED_SOURCES += [
-        'MenuBoxObject.cpp',
         'nsDeckFrame.cpp',
         'nsDocElementBoxFrame.cpp',
         'nsGroupBoxFrame.cpp',
         'nsImageBoxFrame.cpp',
         'nsLeafBoxFrame.cpp',
         'nsMenuBarFrame.cpp',
         'nsMenuBarListener.cpp',
         'nsMenuFrame.cpp',
--- a/layout/xul/nsDeckFrame.cpp
+++ b/layout/xul/nsDeckFrame.cpp
@@ -22,16 +22,17 @@
 #include "nsIPresShell.h"
 #include "nsCSSRendering.h"
 #include "nsViewManager.h"
 #include "nsBoxLayoutState.h"
 #include "nsStackLayout.h"
 #include "nsDisplayList.h"
 #include "nsContainerFrame.h"
 #include "nsContentUtils.h"
+#include "nsXULPopupManager.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 nsIFrame*
 NS_NewDeckFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
 {
--- a/layout/xul/nsProgressMeterFrame.cpp
+++ b/layout/xul/nsProgressMeterFrame.cpp
@@ -59,17 +59,17 @@ nsReflowFrameRunnable::Run()
 }
 
 //
 // NS_NewToolbarFrame
 //
 // Creates a new Toolbar frame and returns it
 //
 nsIFrame*
-NS_NewProgressMeterFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle)
+NS_NewProgressMeterFrame (nsIPresShell* aPresShell, mozilla::ComputedStyle* aStyle)
 {
   return new (aPresShell) nsProgressMeterFrame(aStyle);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsProgressMeterFrame)
 
 //
 // nsProgressMeterFrame dstr
--- a/toolkit/content/tests/chrome/popup_trigger.js
+++ b/toolkit/content/tests/chrome/popup_trigger.js
@@ -24,17 +24,17 @@ function cacheEvent(modifiers) {
 function runTests() {
   if (screen.height < 768) {
     ok(false, "popup tests are likely to fail for screen heights less than 768 pixels");
   }
 
   gMenuPopup = document.getElementById("thepopup");
   gTrigger = document.getElementById("trigger");
 
-  gIsMenu = gTrigger.boxObject instanceof MenuBoxObject;
+  gIsMenu = gTrigger.hasMenu();
 
   // a hacky way to get the screen position of the document. Cache the event
   // so that we can use it in calls to openPopup.
   gCachedEvent = cacheEvent({ shiftKey: true });
   gScreenX = gCachedEvent.screenX;
   gScreenY = gCachedEvent.screenY;
   gCachedEvent2 = cacheEvent({ altKey: true, ctrlKey: true, shiftKey: true, metaKey: true });
 
--- a/toolkit/content/tests/chrome/test_contextmenu_list.xul
+++ b/toolkit/content/tests/chrome/test_contextmenu_list.xul
@@ -101,17 +101,17 @@ function startTest()
 function menuTests()
 {
   gSelectionStep = 0;
   var element = $("menu");
   synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
   is(gContextMenuFired, true, "context menu fired when menu open");
 
   gSelectionStep = 1;
-  $("menu").boxObject.activeChild = $("menu2");
+  $("menu").activeChild = $("menu2");
   synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
 
   $("menu").open = false;
 }
 
 function nextTest()
 {
   gTestId++;
--- a/toolkit/content/tests/chrome/test_menulist.xul
+++ b/toolkit/content/tests/chrome/test_menulist.xul
@@ -213,17 +213,17 @@ function test_menulist_open(element, scr
 */
 
   // bug 543065, hovering the mouse over an item should highlight it, not
   // scroll the parent, and not change the selected index.
   var item = element.menupopup.childNodes[1];
 
   synthesizeMouse(element.menupopup.childNodes[1], 2, 2, { type: "mousemove" });
   synthesizeMouse(element.menupopup.childNodes[1], 6, 6, { type: "mousemove" });
-  is(element.menuBoxObject.activeChild, item, "activeChild after menu highlight " + element.id);
+  is(element.activeChild, item, "activeChild after menu highlight " + element.id);
   is(element.selectedIndex, 0, "selectedIndex after menu highlight " + element.id);
   is(scroller.scrollTop, 0, "scroll position after menu highlight " + element.id);
 
   element.open = false;
 }
 
 function checkScrollAndFinish()
 {
--- a/toolkit/content/tests/chrome/test_menulist_keynav.xul
+++ b/toolkit/content/tests/chrome/test_menulist_keynav.xul
@@ -55,21 +55,21 @@ function runTests()
     keyCheck(list, "KEY_ArrowDown", 3, 1, "cursor down skip disabled");
     keyCheck(list, "KEY_ArrowUp", 2, 1, "cursor up skip disabled");
     keyCheck(list, "KEY_ArrowUp", 1, 1, "cursor up");
 
     // On Windows, wrapping doesn't occur.
     keyCheck(list, "KEY_ArrowUp", iswin ? 1 : 4, 1, "cursor up wrap");
 
     list.selectedIndex = 4;
-    list.menuBoxObject.activeChild = list.selectedItem;
+    list.activeChild = list.selectedItem;
     keyCheck(list, "KEY_ArrowDown", iswin ? 4 : 1, 4, "cursor down wrap");
 
     list.selectedIndex = 0;
-    list.menuBoxObject.activeChild = list.selectedItem;
+    list.activeChild = list.selectedItem;
   }
 
   // check that attempting to open the menulist does not change the selection
   synthesizeKey("KEY_ArrowDown", {altKey: !ismac});
   is(list.selectedItem, $("i1"), "open menulist down selectedItem");
   synthesizeKey("KEY_ArrowUp", {altKey: !ismac});
   is(list.selectedItem, $("i1"), "open menulist up selectedItem");
 
@@ -166,17 +166,17 @@ function tabAndScroll()
   list.open = true;
 
   var rowdiff = list.getItemAtIndex(1).getBoundingClientRect().top -
                 list.getItemAtIndex(0).getBoundingClientRect().top;
 
   var item = list.getItemAtIndex(10);
   var originalPosition = item.getBoundingClientRect().top;
 
-  list.menuBoxObject.activeChild = item;
+  list.activeChild = item;
   ok(item.getBoundingClientRect().top < originalPosition,
     "position of item 1: " + item.getBoundingClientRect().top + " -> " + originalPosition);
 
   originalPosition = item.getBoundingClientRect().top;
 
   synthesizeKey("KEY_ArrowDown");
   is(item.getBoundingClientRect().top, originalPosition - rowdiff, "position of item 10");
 
@@ -252,30 +252,30 @@ function checkCursorNavigation()
   is(list.menupopup.state, "open", "cursor down popup state");
   synthesizeKey("KEY_PageDown");
   is(list.selectedIndex, iswin ? 3 : 1, "selectedIndex after page down");
   is(commandEventsCount, iswin ? 2 : 0, "selectedIndex after page down command event");
   is(list.menupopup.state, "open", "page down popup state");
 
   // Check whether cursor up and down wraps.
   list.selectedIndex = 0;
-  list.menuBoxObject.activeChild = list.selectedItem;
+  list.activeChild = list.selectedItem;
   synthesizeKey("KEY_ArrowUp");
-  is(list.menuBoxObject.activeChild,
+  is(list.activeChild,
      document.getElementById(iswin || ismac ? "b1" : "b4"), "cursor up wrap while open");
 
   list.selectedIndex = 3;
-  list.menuBoxObject.activeChild = list.selectedItem;
+  list.activeChild = list.selectedItem;
   synthesizeKey("KEY_ArrowDown");
-  is(list.menuBoxObject.activeChild,
+  is(list.activeChild,
      document.getElementById(iswin || ismac ? "b4" : "b1"), "cursor down wrap while open");
 
   synthesizeKey("KEY_ArrowUp", {altKey: true});
   is(list.open, ismac, "alt+up closes popup");
- 
+
   if (ismac) {
     list.open = false;
   }
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForFocus(runTests);
--- a/toolkit/content/tests/chrome/test_menulist_paging.xul
+++ b/toolkit/content/tests/chrome/test_menulist_paging.xul
@@ -119,36 +119,36 @@ function runTest()
 
   test = tests.shift();
   document.getElementById(test.list).open = true;
 }
 
 function menulistShown()
 {
   let menulist = document.getElementById(test.list);
-  is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.initial).label, test.list + " initial selection");
+  is(menulist.activeChild.label, menulist.getItemAtIndex(test.initial).label, test.list + " initial selection");
 
   let cs = window.getComputedStyle(menulist.menupopup);
   let bpTop = parseFloat(cs.paddingTop) + parseFloat(cs.borderTopWidth);
 
   // Skip menulist3 as it has a label that scrolling doesn't need normally deal with.
   if (test.scroll >= 0) {
     is(menulist.menupopup.childNodes[test.scroll].getBoundingClientRect().top,
        menulist.menupopup.getBoundingClientRect().top + bpTop,
        "Popup scroll at correct position");
   }
 
   for (let i = 0; i < test.downs.length; i++) {
     sendKey("PAGE_DOWN");
-    is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.downs[i]).label, test.list + " page down " + i);
+    is(menulist.activeChild.label, menulist.getItemAtIndex(test.downs[i]).label, test.list + " page down " + i);
   }
 
   for (let i = 0; i < test.ups.length; i++) {
     sendKey("PAGE_UP");
-    is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.ups[i]).label, test.list + " page up " + i);
+    is(menulist.activeChild.label, menulist.getItemAtIndex(test.ups[i]).label, test.list + " page up " + i);
   }
 
   menulist.open = false;
 }
 ]]>
 </script>
 
 <body xmlns="http://www.w3.org/1999/xhtml">
--- a/toolkit/content/tests/widgets/popup_shared.js
+++ b/toolkit/content/tests/widgets/popup_shared.js
@@ -247,34 +247,30 @@ function goNextStepSync() {
   } else {
     finish();
   }
 }
 
 function openMenu(menu) {
   if ("open" in menu) {
     menu.open = true;
+  } else if (menu.hasMenu()) {
+    menu.openMenu(true);
   } else {
-    var bo = menu.boxObject;
-    if (bo instanceof MenuBoxObject)
-      bo.openMenu(true);
-    else
-      synthesizeMouse(menu, 4, 4, { });
+    synthesizeMouse(menu, 4, 4, { });
   }
 }
 
 function closeMenu(menu, popup) {
   if ("open" in menu) {
     menu.open = false;
+  } else if (menu.hasMenu()) {
+    menu.openMenu(false);
   } else {
-    var bo = menu.boxObject;
-    if (bo instanceof MenuBoxObject)
-      bo.openMenu(false);
-    else
-      popup.hidePopup();
+    popup.hidePopup();
   }
 }
 
 function checkActive(popup, id, testname) {
   var activeok = true;
   var children = popup.childNodes;
   for (var c = 0; c < children.length; c++) {
     var child = children[c];
@@ -286,25 +282,25 @@ function checkActive(popup, id, testname
   }
   ok(activeok, testname + " item " + (id ? id : "none") + " active");
 }
 
 function checkOpen(menuid, testname) {
   var menu = document.getElementById(menuid);
   if ("open" in menu)
     ok(menu.open, testname + " " + menuid + " menu is open");
-  else if (menu.boxObject instanceof MenuBoxObject)
+  else if (menu.hasMenu())
     ok(menu.getAttribute("open") == "true", testname + " " + menuid + " menu is open");
 }
 
 function checkClosed(menuid, testname) {
   var menu = document.getElementById(menuid);
   if ("open" in menu)
     ok(!menu.open, testname + " " + menuid + " menu is open");
-  else if (menu.boxObject instanceof MenuBoxObject)
+  else if (menu.hasMenu())
     ok(!menu.hasAttribute("open"), testname + " " + menuid + " menu is closed");
 }
 
 function convertPosition(anchor, align) {
   if (anchor == "topleft" && align == "topleft") return "overlap";
   if (anchor == "topleft" && align == "topright") return "start_before";
   if (anchor == "topleft" && align == "bottomleft") return "before_start";
   if (anchor == "topright" && align == "topleft") return "end_before";
--- a/toolkit/content/widgets/button.xml
+++ b/toolkit/content/widgets/button.xml
@@ -20,18 +20,18 @@
                 onset="this.setAttribute('dlgtype', val); return val;"/>
 
       <property name="group"
                 onget="return this.getAttribute('group');"
                 onset="this.setAttribute('group', val); return val;"/>
 
       <property name="open" onget="return this.hasAttribute('open');">
         <setter><![CDATA[
-          if (this.boxObject instanceof MenuBoxObject) {
-            this.boxObject.openMenu(val);
+          if (this.hasMenu()) {
+            this.openMenu(val);
           } else if (val) {
             // Fall back to just setting the attribute
             this.setAttribute("open", "true");
           } else {
             this.removeAttribute("open");
           }
           return val;
         ]]></setter>
@@ -123,17 +123,17 @@
         this._handleClick();
         // Prevent page from scrolling on the space key.
         event.preventDefault();
       ]]>
       </handler>
 
       <handler event="keypress">
       <![CDATA[
-        if (this.boxObject instanceof MenuBoxObject) {
+        if (this.hasMenu()) {
           if (this.open)
             return;
         } else {
           if (event.keyCode == KeyEvent.DOM_VK_UP ||
               (event.keyCode == KeyEvent.DOM_VK_LEFT &&
                 document.defaultView.getComputedStyle(this.parentNode)
                         .direction == "ltr") ||
               (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
--- a/toolkit/content/widgets/menu.xml
+++ b/toolkit/content/widgets/menu.xml
@@ -43,27 +43,21 @@
   </binding>
 
   <binding id="menu-base"
            extends="chrome://global/content/bindings/menu.xml#menuitem-base">
 
     <implementation implements="nsIDOMXULContainerElement">
       <property name="open" onget="return this.hasAttribute('open');">
         <setter><![CDATA[
-          this.boxObject.openMenu(val);
+          this.openMenu(val);
           return val;
         ]]></setter>
       </property>
 
-      <property name="openedWithKey" readonly="true">
-        <getter><![CDATA[
-          return this.boxObject.openedWithKey;
-        ]]></getter>
-      </property>
-
       <!-- nsIDOMXULContainerElement interface -->
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <body>
           var menupopup = this.menupopup;
           if (!menupopup) {
             menupopup = this.ownerDocument.createXULElement("menupopup");
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -24,52 +24,51 @@
 
     <handlers>
       <handler event="command" phase="capturing"
         action="if (event.target.parentNode.parentNode == this) this.selectedItem = event.target;"/>
 
       <handler event="popupshowing">
         <![CDATA[
           if (event.target.parentNode == this) {
-            this.menuBoxObject.activeChild = null;
+            this.activeChild = null;
             if (this.selectedItem)
               // Not ready for auto-setting the active child in hierarchies yet.
               // For now, only do this when the outermost menupopup opens.
-              this.menuBoxObject.activeChild = this.mSelectedInternal;
+              this.activeChild = this.mSelectedInternal;
           }
         ]]>
       </handler>
 
       <handler event="keypress" modifiers="shift any" group="system">
         <![CDATA[
           if (!event.defaultPrevented &&
               (event.keyCode == KeyEvent.DOM_VK_UP ||
                event.keyCode == KeyEvent.DOM_VK_DOWN ||
                event.keyCode == KeyEvent.DOM_VK_PAGE_UP ||
                event.keyCode == KeyEvent.DOM_VK_PAGE_DOWN ||
                event.keyCode == KeyEvent.DOM_VK_HOME ||
                event.keyCode == KeyEvent.DOM_VK_END ||
                event.keyCode == KeyEvent.DOM_VK_BACK_SPACE ||
                event.charCode > 0)) {
             // Moving relative to an item: start from the currently selected item
-            this.menuBoxObject.activeChild = this.mSelectedInternal;
-            if (this.menuBoxObject.handleKeyPress(event)) {
-              this.menuBoxObject.activeChild.doCommand();
+            this.activeChild = this.mSelectedInternal;
+            if (this.handleKeyPress(event)) {
+              this.activeChild.doCommand();
               event.preventDefault();
             }
           }
         ]]>
       </handler>
     </handlers>
 
     <implementation implements="nsIDOMXULMenuListElement">
       <constructor>
         this.mSelectedInternal = null;
         this.mAttributeObserver = null;
-        this.menuBoxObject = this.boxObject;
         this.setInitialSelection();
       </constructor>
 
       <method name="setInitialSelection">
         <body>
           <![CDATA[
             var popup = this.menupopup;
             if (popup) {
@@ -116,18 +115,17 @@
       <property name="crop" onset="this.setAttribute('crop',val); return val;"
                             onget="return this.getAttribute('crop');"/>
       <property name="image"  onset="this.setAttribute('image',val); return val;"
                               onget="return this.getAttribute('image');"/>
       <property name="label" readonly="true" onget="return this.getAttribute('label');"/>
       <property name="description" onset="this.setAttribute('description',val); return val;"
                                    onget="return this.getAttribute('description');"/>
 
-      <property name="open" onset="this.menuBoxObject.openMenu(val);
-                                   return val;"
+      <property name="open" onset="this.openMenu(val); return val;"
                             onget="return this.hasAttribute('open');"/>
 
       <property name="itemCount" readonly="true"
                 onget="return this.menupopup ? this.menupopup.children.length : 0"/>
 
       <property name="menupopup" readonly="true">
         <getter>
           <![CDATA[
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -427,17 +427,17 @@ function populateChildren(menulist, opti
         // _moz-menuactive attribute on the selected <xul:menuitem>.
         menulist.selectedItem = item;
 
         // It's hack time. In the event that we've re-populated the menulist due
         // to a mutation in the <select> in content, that means that the -moz_activemenu
         // may have been removed from the selected item. Since that's normally only
         // set for the initially selected on popupshowing for the menulist, and we
         // don't want to close and re-open the popup, we manually set it here.
-        menulist.menuBoxObject.activeChild = item;
+        menulist.activeChild = item;
       }
 
       item.setAttribute("value", option.index);
 
       if (parentElement) {
         item.classList.add("contentSelectDropdown-ingroup");
       }
     }
@@ -466,25 +466,25 @@ function populateChildren(menulist, opti
           searchbox.parentElement.hidePopup();
           break;
         case "ArrowDown":
         case "Enter":
         case "Tab":
           searchbox.blur();
           if (searchbox.nextElementSibling.localName == "menuitem" &&
               !searchbox.nextElementSibling.hidden) {
-            menulist.menuBoxObject.activeChild = searchbox.nextElementSibling;
+            menulist.activeChild = searchbox.nextElementSibling;
           } else {
             var currentOption = searchbox.nextElementSibling;
             while (currentOption && (currentOption.localName != "menuitem" ||
                   currentOption.hidden)) {
               currentOption = currentOption.nextElementSibling;
             }
             if (currentOption) {
-              menulist.menuBoxObject.activeChild = currentOption;
+              menulist.activeChild = currentOption;
             } else {
               searchbox.focus();
             }
           }
           break;
         default:
           return;
       }
@@ -551,17 +551,17 @@ function onSearchInput() {
       }
     }
   }
 }
 
 function onSearchFocus() {
   let searchObj = this;
   let menupopup = searchObj.parentElement;
-  menupopup.parentElement.menuBoxObject.activeChild = null;
+  menupopup.parentElement.activeChild = null;
   menupopup.setAttribute("ignorekeys", "true");
   currentBrowser.messageManager.sendAsyncMessage("Forms:SearchFocused", {});
 }
 
 function onSearchBlur() {
   let searchObj = this;
   let menupopup = searchObj.parentElement;
   menupopup.setAttribute("ignorekeys", "false");