Bug 1465219 use XULMenuElement instead of MenuBoxObject draft
authorEmma Malysz <emalysz@mozilla.com>
Tue, 31 Jul 2018 12:30:17 -0700
changeset 828279 684b5e916803c760afee537740e1847ee168e436
parent 824743 5a5fb40fb92245de198f4fc48c1187a2b5abd02a
push id118670
push userbmo:emalysz@mozilla.com
push dateFri, 10 Aug 2018 18:31:49 +0000
bugs1465219
milestone63.0a1
Bug 1465219 use XULMenuElement instead of MenuBoxObject MozReview-Commit-ID: 5253hAlxbhw
browser/base/content/test/forms/browser_selectpopup.js
browser/base/content/test/general/browser_bug647886.js
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/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
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_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/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.firstChild.localName, "menucaption", "optgroup is caption");
   is(selectPopup.firstChild.getAttribute("label"), "First Group", "optgroup label");
   is(selectPopup.childNodes[1].localName, "menuitem", "option is menuitem");
   is(selectPopup.childNodes[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/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -251,17 +251,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 "nsIDocShellTreeOwner.h"
 #endif
 #include "nsIPresShellInlines.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
@@ -6393,19 +6392,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
@@ -44,16 +44,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"
@@ -3859,16 +3860,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;
       }
     }
 
     if (!cb) {
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;
+  readonly attribute boolean openWithKey;
 
 };
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -42,16 +42,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
@@ -655,18 +655,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!
@@ -1249,16 +1247,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
@@ -3,16 +3,18 @@
  * 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 XULControllers;
 
 [HTMLConstructor, Func="IsChromeOrXBL"]
 interface XULElement : Element {
+
+  void openMenu(boolean openFlag);
   // Layout properties
   [SetterThrows]
   attribute DOMString align;
   [SetterThrows]
   attribute DOMString dir;
   [SetterThrows]
   attribute DOMString flex;
   [SetterThrows]
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -655,17 +655,16 @@ WEBIDL_FILES = [
     'MediaStream.webidl',
     'MediaStreamAudioDestinationNode.webidl',
     'MediaStreamAudioSourceNode.webidl',
     'MediaStreamError.webidl',
     'MediaStreamTrack.webidl',
     'MediaTrackConstraintSet.webidl',
     'MediaTrackSettings.webidl',
     'MediaTrackSupportedConstraints.webidl',
-    'MenuBoxObject.webidl',
     'MessageChannel.webidl',
     'MessageEvent.webidl',
     'MessagePort.webidl',
     'MIDIAccess.webidl',
     'MIDIInput.webidl',
     'MIDIInputMap.webidl',
     'MIDIMessageEvent.webidl',
     'MIDIOptions.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,71 @@
 /* -*- 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* MenuBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return MenuBoxObject_Binding::Wrap(aCx, this, aGivenProto);
-}
-
-void MenuBoxObject::OpenMenu(bool aOpenFlag)
+JSObject*
+XULMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  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);
-        }
-      }
-    }
-  }
+  return XULMenuElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 already_AddRefed<Element>
-MenuBoxObject::GetActiveChild()
+XULMenuElement::GetActiveChild()
 {
-  nsMenuFrame* menu = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
   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(GetPrimaryFrame());
   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(GetPrimaryFrame());
   if (!menu) {
     return false;
   }
 
   nsMenuPopupFrame* popupFrame = menu->GetPopup();
   if (!popupFrame) {
     return false;
   }
@@ -110,19 +81,19 @@ 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::OpenWithKey()
 {
-  nsMenuFrame* menuframe = do_QueryFrame(GetFrame(false));
+  nsMenuFrame* menuframe = do_QueryFrame(GetPrimaryFrame());
   if (!menuframe) {
     return false;
   }
 
   nsIFrame* frame = menuframe->GetParent();
   while (frame) {
     nsMenuBarFrame* menubar = do_QueryFrame(frame);
     if (menubar) {
@@ -130,19 +101,8 @@ bool MenuBoxObject::OpenedWithKey()
     }
     frame = frame->GetParent();
   }
   return false;
 }
 
 } // namespace dom
 } // namespace mozilla
-
-// Creation Routine ///////////////////////////////////////////////////////////////////////
-
-using namespace mozilla::dom;
-
-nsresult
-NS_NewMenuBoxObject(nsIBoxObject** aResult)
-{
-  NS_ADDREF(*aResult = new MenuBoxObject());
-  return NS_OK;
-}
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,39 @@
 /* -*- 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();
+  explicit XULMenuElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo): nsXULElement(aNodeInfo)
+  {
+  }
 
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  void OpenMenu(bool aOpenFlag);
+  // void OpenMenu(bool aOpenFlag);
   already_AddRefed<Element> GetActiveChild();
   void SetActiveChild(Element* arg);
   bool HandleKeyPress(KeyboardEvent& keyEvent);
-  bool OpenedWithKey();
+  bool OpenWithKey();
 
 private:
-  ~MenuBoxObject();
+  virtual ~XULMenuElement() {}
+  JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) final;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_MenuBoxObject_h
+#endif // XULMenuElement_h
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -16,31 +16,33 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chr
 
 if CONFIG['MOZ_XUL']:
     EXPORTS += [
         'nsXULElement.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',
     'nsIXULSortService.idl',
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -68,16 +68,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"
@@ -152,16 +153,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,39 @@ nsXULElement::IsFocusableInternal(int32_
     } else {
       shouldFocus = *aTabIndex >= 0;
     }
   }
 
   return shouldFocus;
 }
 
+void
+nsXULElement::OpenMenu(bool aOpenFlag)
+{
+  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+  if (pm) {
+    nsIFrame* frame = GetPrimaryFrame();
+    if (frame) {
+      if (aOpenFlag) {
+        // nsCOMPtr<nsIContent> content = mContent;
+        pm->ShowMenu(this, false, false);
+      }
+      else {
+        nsMenuFrame* menu = do_QueryFrame(frame);
+        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;
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -370,16 +370,18 @@ 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
 
+    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/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -14,20 +14,16 @@
 /* a6cf90f9-15b3-11d2-932e-00805f8add32 */
 #define NS_LAYOUT_DEBUGGER_CID \
  { 0xa6cf90f9, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
 
 // {D750A964-2D14-484c-B3AA-8ED7823B5C7B}
 #define NS_BOXOBJECT_CID \
 { 0xd750a964, 0x2d14, 0x484c, { 0xb3, 0xaa, 0x8e, 0xd7, 0x82, 0x3b, 0x5c, 0x7b } }
 
-// {AA40253B-4C42-4056-8132-37BCD07862FD}
-#define NS_MENUBOXOBJECT_CID \
-{ 0xaa40253b, 0x4c42, 0x4056, { 0x81, 0x32, 0x37, 0xbc, 0xd0, 0x78, 0x62, 0xfd } }
-
 // {3B581FD4-3497-426c-8F61-3658B971CB80}
 #define NS_TREEBOXOBJECT_CID \
 { 0x3b581fd4, 0x3497, 0x426c, { 0x8f, 0x61, 0x36, 0x58, 0xb9, 0x71, 0xcb, 0x80 } }
 
 // {2fe88332-31c6-4829-b247-a07d8a73e80f}
 #define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
 { 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -295,17 +295,16 @@ Shutdown()
 
 #ifdef DEBUG
 nsresult NS_NewLayoutDebugger(nsILayoutDebugger** aResult);
 #endif
 
 nsresult NS_NewBoxObject(nsIBoxObject** aResult);
 
 #ifdef MOZ_XUL
-nsresult NS_NewMenuBoxObject(nsIBoxObject** aResult);
 nsresult NS_NewTreeBoxObject(nsIBoxObject** aResult);
 #endif
 
 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult);
 
 already_AddRefed<nsIContentViewer> NS_NewContentViewer();
 nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
 nsresult NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult);
@@ -355,17 +354,16 @@ ctor_(nsISupports* aOuter, REFNSIID aIID
 #ifdef DEBUG
 MAKE_CTOR(CreateNewLayoutDebugger,        nsILayoutDebugger,           NS_NewLayoutDebugger)
 #endif
 
 MAKE_CTOR(CreateNewFrameTraversal,      nsIFrameTraversal,      NS_CreateFrameTraversal)
 MAKE_CTOR(CreateNewBoxObject,           nsIBoxObject,           NS_NewBoxObject)
 
 #ifdef MOZ_XUL
-MAKE_CTOR(CreateNewMenuBoxObject,       nsIBoxObject,           NS_NewMenuBoxObject)
 MAKE_CTOR(CreateNewTreeBoxObject,       nsIBoxObject,           NS_NewTreeBoxObject)
 #endif // MOZ_XUL
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(inDeepTreeWalker)
 
 MAKE_CTOR2(CreateContentViewer,           nsIContentViewer,            NS_NewContentViewer)
 MAKE_CTOR(CreateHTMLDocument,             nsIDocument,                 NS_NewHTMLDocument)
 MAKE_CTOR(CreateXMLDocument,              nsIDocument,                 NS_NewXMLDocument)
@@ -496,17 +494,16 @@ Construct_nsIScriptSecurityManager(nsISu
 }
 
 #ifdef DEBUG
 NS_DEFINE_NAMED_CID(NS_LAYOUT_DEBUGGER_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FRAMETRAVERSAL_CID);
 NS_DEFINE_NAMED_CID(NS_BOXOBJECT_CID);
 #ifdef MOZ_XUL
-NS_DEFINE_NAMED_CID(NS_MENUBOXOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_TREEBOXOBJECT_CID);
 #endif // MOZ_XUL
 NS_DEFINE_NAMED_CID(IN_DEEPTREEWALKER_CID);
 NS_DEFINE_NAMED_CID(NS_CONTENT_VIEWER_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_XMLDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_SVGDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_IMAGEDOCUMENT_CID);
@@ -736,17 +733,16 @@ nsEditingCommandTableConstructor(nsISupp
 static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
   XPCONNECT_CIDENTRIES
 #ifdef DEBUG
   { &kNS_LAYOUT_DEBUGGER_CID, false, nullptr, CreateNewLayoutDebugger },
 #endif
   { &kNS_FRAMETRAVERSAL_CID, false, nullptr, CreateNewFrameTraversal },
   { &kNS_BOXOBJECT_CID, false, nullptr, CreateNewBoxObject },
 #ifdef MOZ_XUL
-  { &kNS_MENUBOXOBJECT_CID, false, nullptr, CreateNewMenuBoxObject },
   { &kNS_TREEBOXOBJECT_CID, false, nullptr, CreateNewTreeBoxObject },
 #endif // MOZ_XUL
   { &kIN_DEEPTREEWALKER_CID, false, nullptr, inDeepTreeWalkerConstructor },
   { &kNS_CONTENT_VIEWER_CID, false, nullptr, CreateContentViewer },
   { &kNS_HTMLDOCUMENT_CID, false, nullptr, CreateHTMLDocument },
   { &kNS_XMLDOCUMENT_CID, false, nullptr, CreateXMLDocument },
   { &kNS_SVGDOCUMENT_CID, false, nullptr, CreateSVGDocument },
   { &kNS_IMAGEDOCUMENT_CID, false, nullptr, CreateImageDocument },
@@ -842,17 +838,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_SCRIPTERROR_CID, false, nullptr, nsScriptErrorConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
-  { "@mozilla.org/layout/xul-boxobject-menu;1", &kNS_MENUBOXOBJECT_CID },
   { "@mozilla.org/layout/xul-boxobject-tree;1", &kNS_TREEBOXOBJECT_CID },
 #endif // MOZ_XUL
   { "@mozilla.org/inspector/deep-tree-walker;1", &kIN_DEEPTREEWALKER_CID },
   { "@mozilla.org/xml/xml-document;1", &kNS_XMLDOCUMENT_CID },
   { "@mozilla.org/svg/svg-document;1", &kNS_SVGDOCUMENT_CID },
   { "@mozilla.org/content/post-content-iterator;1", &kNS_CONTENTITERATOR_CID },
   { "@mozilla.org/content/pre-content-iterator;1", &kNS_PRECONTENTITERATOR_CID },
   { "@mozilla.org/content/subtree-content-iterator;1", &kNS_SUBTREEITERATOR_CID },
--- 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 instanceof XULElement;
 
   // 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_menulist.xul
+++ b/toolkit/content/tests/chrome/test_menulist.xul
@@ -269,17 +269,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,33 +247,29 @@ function goNextStepSync() {
   } else {
     finish();
   }
 }
 
 function openMenu(menu) {
   if ("open" in menu) {
     menu.open = true;
+  } else if (menu instanceof XULElement) {
+      menu.openMenu(true);
   } else {
-    var bo = menu.boxObject;
-    if (bo instanceof MenuBoxObject)
-      bo.openMenu(true);
-    else
       synthesizeMouse(menu, 4, 4, { });
   }
 }
 
 function closeMenu(menu, popup) {
   if ("open" in menu) {
     menu.open = false;
+  } else if (menu instanceof XULElement) {
+      menu.openMenu(false);
   } else {
-    var bo = menu.boxObject;
-    if (bo instanceof MenuBoxObject)
-      bo.openMenu(false);
-    else
       popup.hidePopup();
   }
 }
 
 function checkActive(popup, id, testname) {
   var activeok = true;
   var children = popup.childNodes;
   for (var c = 0; c < children.length; 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 instanceof XULElement)
     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 instanceof XULElement)
     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
@@ -18,30 +18,16 @@
       <property name="dlgType"
                 onget="return this.getAttribute('dlgtype');"
                 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);
-          } else if (val) {
-            // Fall back to just setting the attribute
-            this.setAttribute("open", "true");
-          } else {
-            this.removeAttribute("open");
-          }
-          return val;
-        ]]></setter>
-      </property>
-
       <property name="checked" onget="return this.hasAttribute('checked');">
         <setter><![CDATA[
           if (this.type == "radio" && val) {
             var sibs = this.parentNode.getElementsByAttribute("group", this.group);
             for (var i = 0; i < sibs.length; ++i)
               sibs[i].removeAttribute("checked");
           }
 
@@ -123,17 +109,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 instanceof XULElement) {
           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>
           const XUL_NS =
             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -24,339 +24,46 @@
 
     <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.mInputField = null;
-        this.mSelectedInternal = null;
-        this.mAttributeObserver = null;
-        this.menuBoxObject = this.boxObject;
-        this.setInitialSelection();
-      </constructor>
-
-      <method name="setInitialSelection">
-        <body>
-          <![CDATA[
-            var popup = this.menupopup;
-            if (popup) {
-              var arr = popup.getElementsByAttribute("selected", "true");
-
-              var editable = this.editable;
-              var value = this.value;
-              if (!arr.item(0) && value)
-                arr = popup.getElementsByAttribute(editable ? "label" : "value", value);
-
-              if (arr.item(0))
-                this.selectedItem = arr[0];
-              else if (!editable)
-                this.selectedIndex = 0;
-            }
-          ]]>
-        </body>
-      </method>
-
-      <property name="value" onget="return this.getAttribute('value');">
-        <setter>
-          <![CDATA[
-            // if the new value is null, we still need to remove the old value
-            if (val == null)
-              return this.selectedItem = val;
-
-            var arr = null;
-            var popup = this.menupopup;
-            if (popup)
-              arr = popup.getElementsByAttribute("value", val);
-
-            if (arr && arr.item(0))
-              this.selectedItem = arr[0];
-            else {
-              this.selectedItem = null;
-              this.setAttribute("value", val);
-            }
-
-            return val;
-          ]]>
-        </setter>
-      </property>
-
-      <property name="inputField" readonly="true" onget="return null;"/>
-
-      <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="editable" onget="return this.getAttribute('editable') == 'true';">
-        <setter>
-          <![CDATA[
-            if (!val && this.editable) {
-              // If we were focused and transition from editable to not editable,
-              // focus the parent menulist so that the focus does not get stuck.
-              if (this.inputField == document.activeElement)
-                window.setTimeout(() => this.focus(), 0);
-            }
-
-            this.setAttribute("editable", val);
-            return val;
-          ]]>
-        </setter>
-      </property>
-
-      <property name="open" onset="this.menuBoxObject.openMenu(val);
-                                   return val;"
-                            onget="return this.hasAttribute('open');"/>
-
-      <property name="itemCount" readonly="true"
-                onget="return this.menupopup ? this.menupopup.childNodes.length : 0"/>
-
-      <property name="menupopup" readonly="true">
-        <getter>
-          <![CDATA[
-            var popup = this.firstChild;
-            while (popup && popup.localName != "menupopup")
-              popup = popup.nextSibling;
-            return popup;
-          ]]>
-        </getter>
-      </property>
-
-      <method name="contains">
-        <parameter name="item"/>
-        <body>
-          <![CDATA[
-            if (!item)
-              return false;
-
-            var parent = item.parentNode;
-            return (parent && parent.parentNode == this);
-          ]]>
-        </body>
-      </method>
-
-      <property name="selectedIndex">
-        <getter>
-          <![CDATA[
-            // Quick and dirty. We won't deal with hierarchical menulists yet.
-            if (!this.selectedItem ||
-                !this.mSelectedInternal.parentNode ||
-                this.mSelectedInternal.parentNode.parentNode != this)
-              return -1;
-
-            var children = this.mSelectedInternal.parentNode.childNodes;
-            var i = children.length;
-            while (i--)
-              if (children[i] == this.mSelectedInternal)
-                break;
-
-            return i;
-          ]]>
-        </getter>
-        <setter>
-          <![CDATA[
-            var popup = this.menupopup;
-            if (popup && 0 <= val) {
-              if (val < popup.childNodes.length)
-                this.selectedItem = popup.childNodes[val];
-            } else
-              this.selectedItem = null;
-            return val;
-          ]]>
-        </setter>
-      </property>
-
-      <property name="selectedItem">
-        <getter>
-          <![CDATA[
-            return this.mSelectedInternal;
-          ]]>
-        </getter>
-        <setter>
-          <![CDATA[
-            var oldval = this.mSelectedInternal;
-            if (oldval == val)
-              return val;
-
-            if (val && !this.contains(val))
-              return val;
-
-            if (oldval) {
-              oldval.removeAttribute("selected");
-              this.mAttributeObserver.disconnect();
-            }
-
-            this.mSelectedInternal = val;
-            let attributeFilter = ["value", "label", "image", "description"];
-            if (val) {
-              val.setAttribute("selected", "true");
-              for (let attr of attributeFilter) {
-                if (val.hasAttribute(attr)) {
-                  this.setAttribute(attr, val.getAttribute(attr));
-                } else {
-                  this.removeAttribute(attr);
-                }
-              }
-
-              this.mAttributeObserver = new MutationObserver(this.handleMutation.bind(this));
-              this.mAttributeObserver.observe(val, { attributeFilter });
-            } else {
-              for (let attr of attributeFilter) {
-                this.removeAttribute(attr);
-              }
-            }
-
-            var event = document.createEvent("Events");
-            event.initEvent("select", true, true);
-            this.dispatchEvent(event);
-
-            event = document.createEvent("Events");
-            event.initEvent("ValueChange", true, true);
-            this.dispatchEvent(event);
-
-            return val;
-          ]]>
-        </setter>
-      </property>
-
-      <method name="handleMutation">
-        <parameter name="aRecords"/>
-        <body>
-          <![CDATA[
-            for (let record of aRecords) {
-              let t = record.target;
-              if (t == this.mSelectedInternal) {
-                let attrName = record.attributeName;
-                switch (attrName) {
-                  case "value":
-                  case "label":
-                  case "image":
-                  case "description":
-                    if (t.hasAttribute(attrName)) {
-                      this.setAttribute(attrName, t.getAttribute(attrName));
-                    } else {
-                      this.removeAttribute(attrName);
-                    }
-                }
-              }
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="getIndexOfItem">
-        <parameter name="item"/>
-        <body>
-        <![CDATA[
-          var popup = this.menupopup;
-          if (popup) {
-            var children = popup.childNodes;
-            var i = children.length;
-            while (i--)
-              if (children[i] == item)
-                return i;
-          }
-          return -1;
-        ]]>
-        </body>
-      </method>
-
-      <method name="getItemAtIndex">
-        <parameter name="index"/>
-        <body>
-        <![CDATA[
-          var popup = this.menupopup;
-          if (popup) {
-            var children = popup.childNodes;
-            if (index >= 0 && index < children.length)
-              return children[index];
-          }
-          return null;
-        ]]>
-        </body>
-      </method>
-
-      <method name="appendItem">
-        <parameter name="label"/>
-        <parameter name="value"/>
-        <parameter name="description"/>
-        <body>
-        <![CDATA[
-          const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          var popup = this.menupopup ||
-                      this.appendChild(document.createElementNS(XULNS, "menupopup"));
-          var item = document.createElementNS(XULNS, "menuitem");
-          item.setAttribute("label", label);
-          item.setAttribute("value", value);
-          if (description)
-            item.setAttribute("description", description);
-
-          popup.appendChild(item);
-          return item;
-        ]]>
-        </body>
-      </method>
-
-      <method name="removeAllItems">
-        <body>
-        <![CDATA[
-          this.selectedItem = null;
-          var popup = this.menupopup;
-          if (popup)
-            this.removeChild(popup);
-        ]]>
-        </body>
-      </method>
-
-      <destructor>
-        <![CDATA[
-          if (this.mAttributeObserver) {
-            this.mAttributeObserver.disconnect();
-          }
-        ]]>
-      </destructor>
-    </implementation>
   </binding>
 
   <binding id="menulist-editable" extends="chrome://global/content/bindings/menulist.xml#menulist">
     <content sizetopopup="pref">
       <xul:hbox class="menulist-editable-box textbox-input-box" xbl:inherits="context,disabled,readonly,focused" flex="1">
         <html:input class="menulist-editable-input" anonid="input" allowevents="true"
                     xbl:inherits="value=label,value,disabled,tabindex,readonly,placeholder"/>
       </xul:hbox>
@@ -516,21 +223,21 @@
       <handler event="popupshowing">
         <![CDATA[
           // editable menulists elements aren't in the focus order,
           // so when the popup opens we need to force the focus to the inputField
           if (event.target.parentNode == this) {
             if (document.commandDispatcher.focusedElement != this.inputField)
               this.inputField.focus();
 
-            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">
         <![CDATA[
           // open popup if key is up arrow, down arrow, or F4
           if (!event.ctrlKey && !event.shiftKey) {
--- 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.nextSibling.localName == "menuitem" &&
               !searchbox.nextSibling.hidden) {
-            menulist.menuBoxObject.activeChild = searchbox.nextSibling;
+            menulist.activeChild = searchbox.nextSibling;
           } else {
             var currentOption = searchbox.nextSibling;
             while (currentOption && (currentOption.localName != "menuitem" ||
                   currentOption.hidden)) {
               currentOption = currentOption.nextSibling;
             }
             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");