Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 19 Sep 2018 19:44:24 +0300
changeset 492989 c4e3131d033e72be1b0b8ed70c9860657ef0c37f
parent 492988 80d4b05cbfa93b54e82f0a7dbbd611aa2e35f24e (current diff)
parent 492973 59a6a14bdf0581f375d7b85eed59b9d8e4e8fb6f (diff)
child 492990 5500e4f115fc8c0d196fe77187af3d5d4f07fd03
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)
reviewersmerge
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
Merge mozilla-central to autoland. a=merge CLOSED TREE
dom/base/nsDocument.cpp
dom/webidl/MenuBoxObject.webidl
layout/xul/MenuBoxObject.cpp
layout/xul/MenuBoxObject.h
--- 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/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -54,17 +54,16 @@
 #endif
     <!-- work-around bug 392512 -->
     <command id="Browser:AddBookmarkAs"
              oncommand="PlacesCommandHook.bookmarkPage();"/>
     <!-- The command disabled state must be manually updated through
          PlacesCommandHook.updateBookmarkAllTabsCommand() -->
     <command id="Browser:BookmarkAllTabs"
              oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.uniqueCurrentPages);"/>
-    <command id="Browser:Home"    oncommand="BrowserHome();"/>
     <command id="Browser:Back"    oncommand="BrowserBack();" disabled="true"/>
     <command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
       <observes element="Browser:Back" attribute="disabled"/>
     </command>
     <command id="Browser:Forward" oncommand="BrowserForward();" disabled="true"/>
     <command id="Browser:ForwardOrForwardDuplicate" oncommand="BrowserForward(event);" disabled="true">
       <observes element="Browser:Forward" attribute="disabled"/>
     </command>
@@ -197,17 +196,17 @@
 #else
     <key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="accel" />
     <key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="accel" />
 #endif
 #ifdef XP_UNIX
     <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
     <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
 #endif
-    <key id="goHome" keycode="VK_HOME" command="Browser:Home" modifiers="alt"/>
+    <key id="goHome" keycode="VK_HOME" oncommand="BrowserHome();" modifiers="alt"/>
     <key keycode="VK_F5" command="Browser:Reload"/>
 #ifndef XP_MACOSX
     <key id="showAllHistoryKb" key="&showAllHistoryCmd.commandkey;" command="Browser:ShowAllHistory" modifiers="accel,shift"/>
     <key keycode="VK_F5" command="Browser:ReloadSkipCache" modifiers="accel"/>
     <key id="key_fullScreen" keycode="VK_F11" command="View:FullScreen"/>
 #else
     <key id="key_fullScreen" key="&fullScreenCmd.macCommandKey;" command="View:FullScreen" modifiers="accel,control"/>
     <key id="key_fullScreen_old" key="&fullScreenCmd.macCommandKey;" command="View:FullScreen" modifiers="accel,shift"/>
--- 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/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1294,43 +1294,32 @@ CustomElementRegistry::CallGetCustomInte
       LifecycleGetCustomInterfaceCallback* func =
         definition->mCallbacks->mGetCustomInterfaceCallback.Value();
       JS::Rooted<JSObject*> customInterface(RootingCx());
 
       nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aIID);
       func->Call(aElement, iid, &customInterface);
       JS::Rooted<JSObject*> funcGlobal(RootingCx(), func->CallbackGlobalOrNull());
       if (customInterface && funcGlobal) {
-        RefPtr<nsXPCWrappedJS> wrappedJS;
         AutoJSAPI jsapi;
         if (jsapi.Init(funcGlobal)) {
+          nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
           JSContext* cx = jsapi.cx();
-          nsresult rv =
-            nsXPCWrappedJS::GetNewOrUsed(cx, customInterface,
-                                         NS_GET_IID(nsISupports),
-                                         getter_AddRefs(wrappedJS));
-          if (NS_SUCCEEDED(rv) && wrappedJS) {
-            // Check if the returned object implements the desired interface.
-            nsCOMPtr<nsISupports> retval;
-            if (NS_SUCCEEDED(wrappedJS->QueryInterface(aIID,
-                                                       getter_AddRefs(retval)))) {
-              return retval.forget();
-            }
+
+          nsCOMPtr<nsISupports> wrapper;
+          nsresult rv = xpConnect->WrapJSAggregatedToNative(aElement, cx, customInterface,
+                                                            aIID, getter_AddRefs(wrapper));
+          if (NS_SUCCEEDED(rv)) {
+            return wrapper.forget();
           }
         }
       }
     }
   }
 
-  // Otherwise, check if the element supports the interface directly, and just use that.
-  nsCOMPtr<nsISupports> supports;
-  if (NS_SUCCEEDED(aElement->QueryInterface(aIID, getter_AddRefs(supports)))) {
-    return supports.forget();
-  }
-
   return nullptr;
 }
 
 //-----------------------------------------------------
 // CustomElementReactionsStack
 
 void
 CustomElementReactionsStack::CreateAndPushElementQueue()
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -436,20 +436,17 @@ public:
    * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
    */
   static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
 
   /**
    * To allow native code to call methods of chrome-implemented custom elements,
    * a helper method may be defined in the custom element called
    * 'getCustomInterfaceCallback'. This method takes an IID and returns an
-   * object which implements an XPCOM interface. If there is no
-   * getCustomInterfaceCallback or the callback doesn't return an object,
-   * QueryInterface is called on aElement to see if this interface is
-   * implemented directly.
+   * object which implements an XPCOM interface.
    *
    * This returns null if aElement is not from a chrome document.
    */
   static already_AddRefed<nsISupports> CallGetCustomInterface(
     Element* aElement, const nsIID& aIID);
 
   /**
    * Registers an unresolved custom element that is a candidate for
--- 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"
 
@@ -6544,19 +6543,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());
 }
 
@@ -299,17 +306,27 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mBindingParent);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
     NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
-NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
+
+    nsCOMPtr<nsISupports> iface =
+      CustomElementRegistry::CallGetCustomInterface(this, aIID);
+    if (iface) {
+      iface->QueryInterface(aIID, aInstancePtr);
+      if (*aInstancePtr) {
+        return NS_OK;
+      }
+    }
+
+NS_INTERFACE_MAP_END_INHERITING(Element)
 
 //----------------------------------------------------------------------
 // nsINode interface
 
 nsresult
 nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const
 {
     *aResult = nullptr;
@@ -492,16 +509,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/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -329,19 +329,21 @@ MacOSFontEntry::GetVariationAxes(nsTArra
                       kCTFontVariationAxisDefaultValueKey);
             if (num) {
                 CFNumberGetValue(num, kCFNumberFloat32Type, &defaultValue);
             }
             auto name = (CFStringRef)CFDictionaryGetValue(val,
                             kCTFontVariationAxisNameKey);
             if (name) {
                 CFIndex len = CFStringGetLength(name);
-                axis.mName.SetLength(len);
+                nsAutoString nameStr;
+                nameStr.SetLength(len);
                 CFStringGetCharacters(name, CFRangeMake(0, len),
-                                      (UniChar*)axis.mName.BeginWriting());
+                                      (UniChar*)nameStr.BeginWriting());
+                AppendUTF16toUTF8(nameStr, axis.mName);
             }
             axis.mTag = (uint32_t)tag;
             axis.mMinValue = minValue;
             axis.mMaxValue = maxValue;
             axis.mDefaultValue = defaultValue;
             aVariationAxes.AppendElement(axis);
         }
         CFRelease(axes);
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -346,27 +346,39 @@ BinASTParser<Tok>::checkBinding(JSAtom* 
 }
 
 // Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
 // 3.1.5 CheckPositionalParameterIndices.
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> positionalParams,
                                                    ListNode* params)
 {
-    MOZ_ASSERT(positionalParams.get().length() == params->count());
+#ifdef DEBUG
+    // positionalParams should have the same length as non-rest parameters.
+    size_t paramsCount = params->count();
+    if (paramsCount > 0) {
+        if (params->last()->isKind(ParseNodeKind::Spread)) {
+            paramsCount--;
+        }
+    }
+    MOZ_ASSERT(positionalParams.get().length() == paramsCount);
+#endif
 
     uint32_t i = 0;
     for (ParseNode* param : params->contents()) {
         if (param->isKind(ParseNodeKind::Assign)) {
             param = param->as<AssignmentNode>().left();
         }
+        if (param->isKind(ParseNodeKind::Spread)) {
+            continue;
+        }
+
         MOZ_ASSERT(param->isKind(ParseNodeKind::Name) ||
                    param->isKind(ParseNodeKind::Object) ||
-                   param->isKind(ParseNodeKind::Array) ||
-                   param->isKind(ParseNodeKind::Spread));
+                   param->isKind(ParseNodeKind::Array));
 
         if (JSAtom* name = positionalParams.get()[i]) {
             // Simple or default parameter.
             if (param->isKind(ParseNodeKind::Object) || param->isKind(ParseNodeKind::Array)) {
                 return raiseError("AssertedPositionalParameterName: expected positional parameter, got destructuring parameter");
             }
             if (param->isKind(ParseNodeKind::Spread)) {
                 return raiseError("AssertedPositionalParameterName: expected positional parameter, got rest parameter");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/recompute-wrappers.js
@@ -0,0 +1,5 @@
+var g = newGlobal();
+var w1 = g.Math;
+var w2 = g.evaluate("new Array");
+recomputeWrappers(this, g);
+recomputeWrappers();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6133,16 +6133,56 @@ NukeAllCCWs(JSContext* cx, unsigned argc
 
     NukeCrossCompartmentWrappers(cx, AllCompartments(), cx->compartment(),
                                  NukeWindowReferences, NukeAllReferences);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
+RecomputeWrappers(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() > 2) {
+        JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
+                                  "recomputeWrappers");
+        return false;
+    }
+
+    JS::Compartment* sourceComp = nullptr;
+    if (args.get(0).isObject()) {
+        sourceComp = GetObjectCompartment(UncheckedUnwrap(&args[0].toObject()));
+    }
+
+    JS::Compartment* targetComp = nullptr;
+    if (args.get(1).isObject()) {
+        targetComp = GetObjectCompartment(UncheckedUnwrap(&args[1].toObject()));
+    }
+
+    struct SingleOrAllCompartments final : public CompartmentFilter {
+        JS::Compartment* comp;
+        explicit SingleOrAllCompartments(JS::Compartment* c) : comp(c) {}
+        virtual bool match(JS::Compartment* c) const override {
+            return !comp || comp == c;
+        }
+    };
+
+    if (!js::RecomputeWrappers(cx,
+                               SingleOrAllCompartments(sourceComp),
+                               SingleOrAllCompartments(targetComp)))
+    {
+        return false;
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
 GetMaxArgs(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(ARGS_LENGTH_MAX);
     return true;
 }
 
 static bool
@@ -8191,16 +8231,22 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
     JS_FN_HELP("nukeCCW", NukeCCW, 1, 0,
 "nukeCCW(wrapper)",
 "  Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
 
     JS_FN_HELP("nukeAllCCWs", NukeAllCCWs, 0, 0,
 "nukeAllCCWs()",
 "  Like nukeCCW, but for all CrossCompartmentWrappers targeting the current compartment."),
 
+    JS_FN_HELP("recomputeWrappers", RecomputeWrappers, 2, 0,
+"recomputeWrappers([src, [target]])",
+"  Recompute all cross-compartment wrappers. src and target are both optional\n"
+"  and can be used to filter source or target compartments: the unwrapped\n"
+"  object's compartment is used as CompartmentFilter.\n"),
+
     JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0,
 "wrapWithProto(obj)",
 "  Wrap an object into a noop wrapper with prototype semantics."),
 
     JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0,
 "createMappedArrayBuffer(filename, [offset, [size]])",
 "  Create an array buffer that mmaps the given file."),
 
--- 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/taskcluster/ci/source-test/jsshell.yml
+++ b/taskcluster/ci/source-test/jsshell.yml
@@ -1,14 +1,14 @@
 job-defaults:
     platform: linux64/opt
     require-build: true
     worker-type:
         by-platform:
-            linux64.*: releng-hardware/gecko-t-linux-talos
+            linux64.*: releng-hardware/gecko-t-linux-talos-tw
     worker:
         by-platform:
             linux64.*:
                 env:
                     by-shell:
                         sm:
                             SHELL: /bin/bash
                             JSSHELL: /home/cltbld/fetches/js
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -1003,17 +1003,17 @@ def set_worker_type(config, tests):
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-perf-p2'
             else:
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-p2'
         elif test_platform.startswith('android-em-7.0-x86'):
             test['worker-type'] = 'terraform-packet/gecko-t-linux'
         elif test_platform.startswith('linux') or test_platform.startswith('android'):
             if test.get('suite', '') in ['talos', 'raptor'] and \
                  not test['build-platform'].startswith('linux64-ccov'):
-                test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
+                test['worker-type'] = 'releng-hardware/gecko-t-linux-talos-tw'
             else:
                 test['worker-type'] = LINUX_WORKER_TYPES[test['instance-size']]
         else:
             raise Exception("unknown test_platform {}".format(test_platform))
 
         yield test
 
 
--- a/taskcluster/taskgraph/util/workertypes.py
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -31,17 +31,17 @@ WORKER_TYPES = {
     'aws-provisioner-v1/gecko-t-win10-64-gpu': ('generic-worker', 'windows'),
     'releng-hardware/gecko-t-win10-64-hw': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win7-32': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win7-32-gpu': ('generic-worker', 'windows'),
     'releng-hardware/gecko-t-win7-32-hw': ('generic-worker', 'windows'),
     'aws-provisioner-v1/taskcluster-generic': ('docker-worker', 'linux'),
     'invalid/invalid': ('invalid', None),
     'invalid/always-optimized': ('always-optimized', None),
-    'releng-hardware/gecko-t-linux-talos': ('native-engine', 'linux'),
+    'releng-hardware/gecko-t-linux-talos-tw': ('native-engine', 'linux'),
     'scriptworker-prov-v1/balrog-dev': ('balrog', None),
     'scriptworker-prov-v1/balrogworker-v1': ('balrog', None),
     'scriptworker-prov-v1/beetmoverworker-v1': ('beetmover', None),
     'scriptworker-prov-v1/pushapk-v1': ('push-apk', None),
     "scriptworker-prov-v1/signing-linux-v1": ('scriptworker-signing', None),
     "scriptworker-prov-v1/shipit": ('shipit', None),
     "scriptworker-prov-v1/shipit-dev": ('shipit', None),
     "scriptworker-prov-v1/treescript-v1": ('treescript', None),
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -105,30 +105,28 @@ class MozXULElement extends XULElement {
     let link = document.createElement("link");
     link.setAttribute("rel", "localization");
     link.setAttribute("href", path);
 
     container.appendChild(link);
   }
 
   /**
-   * Indicate that a class defining an element implements one or more
-   * XPCOM interfaces. The custom element getCustomInterface is added
-   * as well as an implementation of QueryInterface.
+   * Indicate that a class defining a XUL element implements one or more
+   * XPCOM interfaces by adding a getCustomInterface implementation to it.
    *
    * The supplied class should implement the properties and methods of
    * all of the interfaces that are specified.
    *
    * @param cls
    *        The class that implements the interface.
    * @param names
-   *        Array of interface names
+   *        Array of interface names.
    */
   static implementCustomInterface(cls, ifaces) {
-    cls.prototype.QueryInterface = ChromeUtils.generateQI(ifaces);
     cls.prototype.getCustomInterfaceCallback = function getCustomInterfaceCallback(iface) {
       if (ifaces.includes(Ci[Components.interfacesByID[iface.number]])) {
         return getInterfaceProxy(this);
       }
       return null;
     };
   }
 }
--- 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_custom_element_base.xul
+++ b/toolkit/content/tests/chrome/test_custom_element_base.xul
@@ -7,27 +7,30 @@
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
-  <simpleelement id="simple"/>
+  <button id="one"/>
+  <simpleelement id="two" style="-moz-user-focus: normal;"/>
+  <simpleelement id="three" disabled="true" style="-moz-user-focus: normal;"/>
+  <button id="four"/>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
   SimpleTest.waitForExplicitFinish();
 
   async function runTests() {
     ok(MozXULElement, "MozXULElement defined on the window");
     testParseXULToFragment();
-    testCustomInterface();
+    await testCustomInterface();
 
     let htmlWin = await new Promise(resolve => {
       let htmlIframe = document.createElement("iframe");
       htmlIframe.src = "file_empty.xhtml";
       htmlIframe.onload = () => resolve(htmlIframe.contentWindow);
       document.documentElement.appendChild(htmlIframe);
     });
 
@@ -46,45 +49,86 @@
     let deck = document.documentElement.lastChild;
     ok(deck instanceof MozXULElement, "instance of MozXULElement");
     ok(deck instanceof XULElement, "instance of XULElement");
     is(deck.id, "foo", "attribute set");
     is(deck.selectedIndex, "0", "Custom Element is property attached");
     deck.remove();
   }
 
-  function testCustomInterface() {
+  async function testCustomInterface() {
     class SimpleElement extends MozXULElement {
       get disabled() {
-        return false;
+        return this.getAttribute("disabled") == "true";
       }
 
       set disabled(val) {
+        if (val) this.setAttribute("disabled", "true");
+        else this.removeAttribute("disabled");
+        return val;
       }
 
       get tabIndex() {
-        return 0;
+        return parseInt(this.getAttribute("tabIndex")) || 0;
       }
 
       set tabIndex(val) {
+        if (val) this.setAttribute("tabIndex", val);
+        else this.removeAttribute("tabIndex");
+        return val;
       }
     }
 
+    MozXULElement.implementCustomInterface(SimpleElement, [Ci.nsIDOMXULControlElement]);
     customElements.define("simpleelement", SimpleElement);
-    MozXULElement.implementCustomInterface(SimpleElement, [Ci.nsIDOMXULControlElement]);
+
+    let twoElement = document.getElementById("two");
 
     is(document.documentElement.getCustomInterfaceCallback, undefined,
        "No getCustomInterfaceCallback on non-custom element");
-    is(typeof document.getElementById("simple").getCustomInterfaceCallback, "function",
+    is(typeof twoElement.getCustomInterfaceCallback, "function",
        "getCustomInterfaceCallback available on custom element when set");
     try {
       document.documentElement.QueryInterface(Ci.nsIDOMXULControlElement)
       ok(false, "Non-custom element implements custom interface");
     } catch (ex) {
       ok(true, "Non-custom element implements custom interface");
     }
-    ok(document.getElementById("simple").QueryInterface(Ci.nsIDOMXULControlElement),
-       "Implements custom interface");
+
+    // Try various ways to get the custom interface.
+
+    let asControl = twoElement.getCustomInterfaceCallback(Ci.nsIDOMXULControlElement);
+    ok(asControl, twoElement, "getCustomInterface returns interface implementation ");
+
+    asControl = twoElement.QueryInterface(Ci.nsIDOMXULControlElement);
+    ok(asControl, "QueryInterface to nsIDOMXULControlElement");
+    ok(asControl instanceof Node, "Control is a Node");
+
+    // Now make sure that the custom element handles focus/tabIndex as needed by shitfing
+    // focus around and enabling/disabling the simple elements.
+
+    // Enable Full Keyboard Access emulation on Mac
+    await SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
+
+    ok(!twoElement.disabled, "two is enabled");
+    ok(document.getElementById("three").disabled, "three is disabled");
+
+    await SimpleTest.promiseFocus();
+    ok(document.hasFocus(), "has focus");
+    
+    // This should skip the disabled simpleelement.
+    synthesizeKey("VK_TAB");
+    is(document.activeElement.id, "one", "Tab 1");
+    synthesizeKey("VK_TAB");
+    is(document.activeElement.id, "two", "Tab 2");
+    synthesizeKey("VK_TAB");
+    is(document.activeElement.id, "four", "Tab 3");
+
+    twoElement.disabled = true;
+    is(twoElement.getAttribute("disabled"), "true", "two disabled after change");
+
+    synthesizeKey("VK_TAB", { shiftKey: true });
+    is(document.activeElement.id, "one", "Tab 1");
   }
   ]]>
   </script>
 </window>
 
--- 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");