Bug 1446961, move PopupBoxObject to XULPopupElement, a new subclass of XULElement. Remove popup.xml methods, r=paolo,bz
authorNeil Deakin <neil@mozilla.com>
Fri, 27 Apr 2018 11:04:38 -0400
changeset 416134 19b49df2389f777dd45779d89a3f83046c40b3d6
parent 416133 12e768652a88d11b9c09ec63e1a5df00cdab4dc2
child 416135 7fe6dcb67de4cbb2a71b9c26e3bda8eb364f2966
push id33917
push userapavel@mozilla.com
push dateSat, 28 Apr 2018 17:30:55 +0000
treeherdermozilla-central@08f68e2c892c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaolo, bz
bugs1446961
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1446961, move PopupBoxObject to XULPopupElement, a new subclass of XULElement. Remove popup.xml methods, r=paolo,bz Test changes for removal of PopupBoxObject and popup.xml methods, some reflow tests now have different stacks now that they are not going through popup.xml binding methods, test_popupanchor.xul changes due to need to wait for popuppositioned event after resizing. The old code would just adjust the arrow directly when sizeTo was called, but the new code does this through an asynchronous popuppositioned event. Changes to some places that check for XULElement class.
browser/base/content/test/performance/browser_appmenu.js
browser/base/content/test/performance/browser_urlbar_keyed_search.js
browser/base/content/test/performance/browser_urlbar_search.js
devtools/server/actors/inspector/node.js
devtools/server/actors/thread.js
dom/base/nsDocument.cpp
dom/bindings/Bindings.conf
dom/tests/mochitest/general/test_interfaces.js
dom/webidl/PopupBoxObject.webidl
dom/webidl/XULPopupElement.webidl
dom/webidl/moz.build
dom/xul/XULPopupElement.cpp
dom/xul/XULPopupElement.h
dom/xul/moz.build
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
layout/xul/PopupBoxObject.cpp
layout/xul/PopupBoxObject.h
layout/xul/moz.build
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsXULPopupManager.h
toolkit/content/tests/widgets/test_popupanchor.xul
toolkit/content/widgets/popup.xml
toolkit/modules/PopupNotifications.jsm
--- a/browser/base/content/test/performance/browser_appmenu.js
+++ b/browser/base/content/test/performance/browser_appmenu.js
@@ -11,46 +11,36 @@ let gCUITestUtils = new CustomizableUITe
  * reflows to the whitelist, you should be modifying your code to avoid the reflow.
  *
  * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
  * for tips on how to do that.
  */
 const EXPECTED_APPMENU_OPEN_REFLOWS = [
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "openPopup/this._openPopupPromise<@resource:///modules/PanelMultiView.jsm",
     ],
   },
 
   {
     stack: [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
       "adjustArrowPosition@chrome://global/content/bindings/popup.xml",
       "onxblpopuppositioned@chrome://global/content/bindings/popup.xml",
     ],
 
     maxCount: 3, // This number should only ever go down - never up.
   },
 
   {
     stack: [
-      "get_alignmentPosition@chrome://global/content/bindings/popup.xml",
-      "_calculateMaxHeight@resource:///modules/PanelMultiView.jsm",
-      "handleEvent@resource:///modules/PanelMultiView.jsm",
-    ],
-  },
-
-  {
-    stack: [
       "_calculateMaxHeight@resource:///modules/PanelMultiView.jsm",
       "handleEvent@resource:///modules/PanelMultiView.jsm",
     ],
 
-    maxCount: 6, // This number should only ever go down - never up.
+    maxCount: 7, // This number should only ever go down - never up.
   },
 ];
 
 add_task(async function() {
   await ensureNoPreloadedBrowser();
 
   let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
     "anonid", "textbox-input-box").getBoundingClientRect();
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search.js
@@ -18,17 +18,16 @@ requestLongerTimeout(5);
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
 
   {
@@ -69,17 +68,16 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
     maxCount: 6, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
--- a/browser/base/content/test/performance/browser_urlbar_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_search.js
@@ -18,17 +18,16 @@ requestLongerTimeout(5);
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
 
   {
@@ -69,17 +68,16 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
     maxCount: 6, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
@@ -111,17 +109,16 @@ const EXPECTED_REFLOWS_SECOND_OPEN = [
       "invalidate@chrome://global/content/bindings/autocomplete.xml"
     ],
     maxCount: 24, // This number should only ever go down - never up.
   },
 
   // Bug 1359989
   {
     stack: [
-      "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
   },
 ];
 
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -403,17 +403,17 @@ const NodeActor = protocol.ActorClassWit
     let line = 0;
     let native = false;
     let override = listener.override || {};
     let tags = listener.tags || "";
     let type = listener.type || "";
     let url = "";
 
     // If the listener is an object with a 'handleEvent' method, use that.
-    if (listenerDO.class === "Object" || listenerDO.class === "XULElement") {
+    if (listenerDO.class === "Object" || /^XUL\w*Element$/.test(listenerDO.class)) {
       let desc;
 
       while (!desc && listenerDO) {
         desc = listenerDO.getOwnPropertyDescriptor("handleEvent");
         listenerDO = listenerDO.proto;
       }
 
       if (desc && desc.value) {
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1141,17 +1141,17 @@ const ThreadActor = ActorClassWithSpec(t
           listenerForm.isEventHandler = !!node.hasAttribute(handlerName);
         }
         if (node[handlerName]) {
           listenerForm.isEventHandler = !!node[handlerName];
         }
         // Get the Debugger.Object for the listener object.
         let listenerDO = this.globalDebugObject.makeDebuggeeValue(listener);
         // If the listener is an object with a 'handleEvent' method, use that.
-        if (listenerDO.class == "Object" || listenerDO.class == "XULElement") {
+        if (listenerDO.class === "Object" || /^XUL\w*Element$/.test(listenerDO.class)) {
           // For some events we don't have permission to access the
           // 'handleEvent' property when running in content scope.
           if (!listenerDO.unwrap()) {
             continue;
           }
           let heDesc;
           while (!heDesc && listenerDO) {
             heDesc = listenerDO.getOwnPropertyDescriptor("handleEvent");
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -251,17 +251,16 @@
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #ifdef MOZ_XUL
 #include "mozilla/dom/ListBoxObject.h"
 #include "mozilla/dom/MenuBoxObject.h"
-#include "mozilla/dom/PopupBoxObject.h"
 #include "mozilla/dom/ScrollBoxObject.h"
 #include "mozilla/dom/TreeBoxObject.h"
 #endif
 #include "nsIPresShellInlines.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
@@ -6571,21 +6570,16 @@ nsIDocument::GetBoxObjectFor(Element* aE
   }
 
   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::popup ||
-               tag == nsGkAtoms::menupopup ||
-               tag == nsGkAtoms::panel ||
-               tag == nsGkAtoms::tooltip) {
-      boxObject = new PopupBoxObject();
     } else if (tag == nsGkAtoms::tree) {
       boxObject = new TreeBoxObject();
     } else if (tag == nsGkAtoms::listbox) {
       boxObject = new ListBoxObject();
     } else if (tag == nsGkAtoms::scrollbox) {
       boxObject = new ScrollBoxObject();
     } else {
       boxObject = new BoxObject();
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -102,20 +102,16 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::AudioContext',
 },
 
 'BatteryManager': {
     'nativeType': 'mozilla::dom::battery::BatteryManager',
     'headerFile': 'BatteryManager.h'
 },
 
-'BoxObject': {
-    'resultNotAddRefed': ['element'],
-},
-
 'Cache': {
     'implicitJSContext': [ 'add', 'addAll', 'match', 'matchAll', 'put',
                            'delete', 'keys' ],
     'nativeType': 'mozilla::dom::cache::Cache',
 },
 
 'CacheStorage': {
     'implicitJSContext': [ 'match' ],
@@ -686,20 +682,16 @@ DOMInterfaces = {
 'PluginArray': {
     'nativeType': 'nsPluginArray',
 },
 
 'PluginTag': {
     'nativeType': 'nsIPluginTag',
 },
 
-'PopupBoxObject': {
-    'resultNotAddRefed': ['triggerNode', 'anchorNode'],
-},
-
 'Position': {
     'headerFile': 'nsGeoPosition.h'
 },
 
 'PositionError': {
     'headerFile': 'nsGeolocation.h'
 },
 
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -784,18 +784,16 @@ var interfaceNamesInGlobalScope =
     {name: "PluginArray", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PointerEvent", insecureContext: true, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PopStateEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PopupBlockedEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PopupBoxObject", insecureContext: true, xbl: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Presentation", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationAvailability", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationConnection", insecureContext: true, desktop: false, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationConnectionAvailableEvent", insecureContext: true, desktop: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -1264,16 +1262,18 @@ var interfaceNamesInGlobalScope =
     {name: "XSLTProcessor", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {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: "XULPopupElement", insecureContext: true, xbl: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
 function createInterfaceMap(isXBLScope) {
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
rename from dom/webidl/PopupBoxObject.webidl
rename to dom/webidl/XULPopupElement.webidl
--- a/dom/webidl/PopupBoxObject.webidl
+++ b/dom/webidl/XULPopupElement.webidl
@@ -15,18 +15,18 @@ dictionary OpenPopupOptions {
   // true if popup node attributes override position
   boolean attributesOverride = false;
   // triggerEvent the event that triggered this popup (mouse click for example)
   Event? triggerEvent = null;
 };
 
 typedef (DOMString or OpenPopupOptions) StringOrOpenPopupOptions;
 
-[Func="IsChromeOrXBL"]
-interface PopupBoxObject : BoxObject
+[HTMLConstructor, Func="IsChromeOrXBL"]
+interface XULPopupElement : XULElement
 {
   /**
    * Allow the popup to automatically position itself.
    */
   attribute boolean autoPosition;
 
   /**
    * Open the popup relative to a specified node at a specific location.
@@ -79,52 +79,64 @@ interface PopupBoxObject : BoxObject
    * screen coordinates, are given relative to the top left of the primary
    * screen.
    *
    * @param isContextMenu true for context menus, false for other popups
    * @param x horizontal screen position
    * @param y vertical screen position
    * @param triggerEvent the event that triggered this popup (mouse click for example)
    */
-  void openPopupAtScreen(long x, long y,
-                         boolean isContextMenu,
-                         Event? triggerEvent);
+  void openPopupAtScreen(optional long x = 0, optional long y = 0,
+                         optional boolean isContextMenu = false,
+                         optional Event? triggerEvent = null);
 
   /**
    * Open the popup anchored at a specific screen rectangle. This function is
    * similar to openPopup except that that rectangle of the anchor is supplied
    * rather than an element. The anchor rectangle arguments are screen
    * coordinates.
    */
   void openPopupAtScreenRect(optional DOMString position = "",
-                             long x,
-                             long y,
-                             long width,
-                             long height,
-                             boolean isContextMenu,
-                             boolean attributesOverride,
-                             Event? triggerEvent);
+                             optional long x = 0,
+                             optional long y = 0,
+                             optional long width = 0,
+                             optional long height = 0,
+                             optional boolean isContextMenu = false,
+                             optional boolean attributesOverride = false,
+                             optional Event? triggerEvent = null);
 
   /**
    *  Hide the popup if it is open. The cancel argument is used as a hint that
    *  the popup is being closed because it has been cancelled, rather than
    *  something being selected within the panel.
    *
    * @param cancel if true, then the popup is being cancelled.
    */
   void hidePopup(optional boolean cancel = false);
 
   /**
+   * Attribute getter and setter for label.
+   */
+  [SetterThrows]
+  attribute DOMString label;
+
+  /**
+   * Attribute getter and setter for position.
+   */
+  [SetterThrows]
+  attribute DOMString position;
+
+  /**
    * Returns the state of the popup:
    *   closed - the popup is closed
    *   open - the popup is open
    *   showing - the popup is in the process of being shown
    *   hiding - the popup is in the process of being hidden
    */
-  readonly attribute DOMString popupState;
+  readonly attribute DOMString state;
 
   /**
    * The node that triggered the popup. If the popup is not open, will return
    * null.
    */
   readonly attribute Node? triggerNode;
 
   /**
@@ -144,20 +156,20 @@ interface PopupBoxObject : BoxObject
    */
   void moveTo(long left, long top);
 
   /**
    * Move an open popup to the given anchor position. The arguments have the same
    * meaning as the corresponding argument to openPopup. This method has no effect
    * on popups that are not open.
    */
-  void moveToAnchor(Element? anchorElement,
+  void moveToAnchor(optional Element? anchorElement = null,
                     optional DOMString position = "",
-                    long x, long y,
-                    boolean attributesOverride);
+                    optional long x = 0, optional long y = 0,
+                    optional boolean attributesOverride = false);
 
   /**
    * Size the popup to the given dimensions
    */
   void sizeTo(long width, long height);
 
   /** Returns the alignment position where the popup has appeared relative to its
    *  anchor node or point, accounting for any flipping that occurred.
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -716,17 +716,16 @@ WEBIDL_FILES = [
     'PerformanceServerTiming.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'Permissions.webidl',
     'PermissionStatus.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
     'PointerEvent.webidl',
-    'PopupBoxObject.webidl',
     'Position.webidl',
     'PositionError.webidl',
     'Presentation.webidl',
     'PresentationAvailability.webidl',
     'PresentationConnection.webidl',
     'PresentationConnectionList.webidl',
     'PresentationReceiver.webidl',
     'PresentationRequest.webidl',
@@ -948,16 +947,17 @@ WEBIDL_FILES = [
     'XPathEvaluator.webidl',
     'XPathExpression.webidl',
     'XPathNSResolver.webidl',
     'XPathResult.webidl',
     'XSLTProcessor.webidl',
     'XULCommandEvent.webidl',
     'XULDocument.webidl',
     'XULElement.webidl',
+    'XULPopupElement.webidl',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     WEBIDL_FILES += [
         'PeerConnectionImpl.webidl',
         'PeerConnectionImplEnums.webidl',
         'PeerConnectionObserver.webidl',
         'PeerConnectionObserverEnums.webidl',
rename from layout/xul/PopupBoxObject.cpp
rename to dom/xul/XULPopupElement.cpp
--- a/layout/xul/PopupBoxObject.cpp
+++ b/dom/xul/XULPopupElement.cpp
@@ -9,197 +9,197 @@
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsNameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsMenuPopupFrame.h"
 #include "nsView.h"
 #include "mozilla/AppUnits.h"
 #include "mozilla/dom/DOMRect.h"
-#include "mozilla/dom/PopupBoxObject.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
-#include "mozilla/dom/PopupBoxObjectBinding.h"
+#include "mozilla/dom/XULPopupElement.h"
+#include "mozilla/dom/XULPopupElementBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-PopupBoxObject::PopupBoxObject()
+nsXULElement*
+NS_NewXULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
+  return new XULPopupElement(aNodeInfo);
 }
 
-PopupBoxObject::~PopupBoxObject()
+JSObject*
+XULPopupElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
+  return XULPopupElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
-nsIContent* PopupBoxObject::GetParentObject() const
+nsIFrame*
+XULPopupElement::GetFrame(bool aFlushLayout)
 {
-  return BoxObject::GetParentObject();
-}
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
 
-JSObject* PopupBoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return PopupBoxObjectBinding::Wrap(aCx, this, aGivenProto);
+  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(aFlushLayout ? FlushType::Layout : FlushType::Frames);
+  }
+
+  return GetPrimaryFrame();
 }
 
 void
-PopupBoxObject::HidePopup(bool aCancel)
-{
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    pm->HidePopup(mContent, false, true, false, aCancel);
-  }
-}
-
-void
-PopupBoxObject::OpenPopup(Element* aAnchorElement,
-                          const StringOrOpenPopupOptions& aOptions,
-                          int32_t aXPos, int32_t aYPos,
-                          bool aIsContextMenu,
-                          bool aAttributesOverride,
-                          Event* aTriggerEvent)
+XULPopupElement::OpenPopup(Element* aAnchorElement,
+                           const StringOrOpenPopupOptions& aOptions,
+                           int32_t aXPos, int32_t aYPos,
+                           bool aIsContextMenu,
+                           bool aAttributesOverride,
+                           Event* aTriggerEvent)
 {
   nsAutoString position;
   if (aOptions.IsOpenPopupOptions()) {
     const OpenPopupOptions& options = aOptions.GetAsOpenPopupOptions();
     position = options.mPosition;
     aXPos = options.mX;
     aYPos = options.mY;
     aIsContextMenu = options.mIsContextMenu;
     aAttributesOverride = options.mAttributesOverride;
     aTriggerEvent = options.mTriggerEvent;
   }
   else {
     position = aOptions.GetAsString();
   }
 
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
+  if (pm) {
     // As a special case for popups that are menus when no anchor or position are
     // specified, open the popup with ShowMenu instead of ShowPopup so that the
     // popup is aligned with the menu.
-    if (!aAnchorElement && position.IsEmpty() && mContent->GetPrimaryFrame()) {
-      nsMenuFrame* menu = do_QueryFrame(mContent->GetPrimaryFrame()->GetParent());
+    if (!aAnchorElement && position.IsEmpty() && GetPrimaryFrame()) {
+      nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame()->GetParent());
       if (menu) {
         pm->ShowMenu(menu->GetContent(), false, false);
         return;
       }
     }
 
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    pm->ShowPopup(mContent, anchorContent, position, aXPos, aYPos,
+    pm->ShowPopup(this, aAnchorElement, position, aXPos, aYPos,
                   aIsContextMenu, aAttributesOverride, false, aTriggerEvent);
   }
 }
 
 void
-PopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
-                                  bool aIsContextMenu,
-                                  Event* aTriggerEvent)
+XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
+                                   bool aIsContextMenu,
+                                   Event* aTriggerEvent)
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent)
-    pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
+  if (pm) {
+    pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
+  }
 }
 
 void
-PopupBoxObject::OpenPopupAtScreenRect(const nsAString& aPosition,
-                                      int32_t aXPos, int32_t aYPos,
-                                      int32_t aWidth, int32_t aHeight,
-                                      bool aIsContextMenu,
-                                      bool aAttributesOverride,
-                                      Event* aTriggerEvent)
+XULPopupElement::OpenPopupAtScreenRect(const nsAString& aPosition,
+                                       int32_t aXPos, int32_t aYPos,
+                                       int32_t aWidth, int32_t aHeight,
+                                       bool aIsContextMenu,
+                                       bool aAttributesOverride,
+                                       Event* aTriggerEvent)
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mContent) {
-    pm->ShowPopupAtScreenRect(mContent, aPosition,
+  if (pm) {
+    pm->ShowPopupAtScreenRect(this, aPosition,
                               nsIntRect(aXPos, aYPos, aWidth, aHeight),
                               aIsContextMenu, aAttributesOverride, aTriggerEvent);
   }
 }
 
 void
-PopupBoxObject::MoveTo(int32_t aLeft, int32_t aTop)
+XULPopupElement::HidePopup(bool aCancel)
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+  if (pm) {
+    pm->HidePopup(this, false, true, false, aCancel);
+  }
+}
+
+void
+XULPopupElement::MoveTo(int32_t aLeft, int32_t aTop)
+{
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     menuPopupFrame->MoveTo(CSSIntPoint(aLeft, aTop), true);
   }
 }
 
 void
-PopupBoxObject::MoveToAnchor(Element* aAnchorElement,
-                             const nsAString& aPosition,
-                             int32_t aXPos, int32_t aYPos,
-                             bool aAttributesOverride)
+XULPopupElement::MoveToAnchor(Element* aAnchorElement,
+                              const nsAString& aPosition,
+                              int32_t aXPos, int32_t aYPos,
+                              bool aAttributesOverride)
 {
-  if (mContent) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-
-    nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-    if (menuPopupFrame && menuPopupFrame->IsVisible()) {
-      menuPopupFrame->MoveToAnchor(anchorContent, aPosition, aXPos, aYPos, aAttributesOverride);
-    }
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
+  if (menuPopupFrame && menuPopupFrame->IsVisible()) {
+    menuPopupFrame->MoveToAnchor(aAnchorElement, aPosition, aXPos, aYPos, aAttributesOverride);
   }
 }
 
 void
-PopupBoxObject::SizeTo(int32_t aWidth, int32_t aHeight)
+XULPopupElement::SizeTo(int32_t aWidth, int32_t aHeight)
 {
-  if (!mContent)
-    return;
-
   nsAutoString width, height;
   width.AppendInt(aWidth);
   height.AppendInt(aHeight);
 
-  RefPtr<Element> element = mContent->AsElement();
+  nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference
 
   // We only want to pass aNotify=true to SetAttr once, but must make sure
   // we pass it when a value is being changed.  Thus, we check if the height
   // is the same and if so, pass true when setting the width.
-  bool heightSame = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters);
+  bool heightSame = AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters);
 
-  element->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame);
-  element->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true);
+  SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame);
+  SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true);
 
   // If the popup is open, force a reposition of the popup after resizing it
   // with notifications set to true so that the popuppositioned event is fired.
-  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupShown) {
     menuPopupFrame->SetPopupPosition(nullptr, false, false, true);
   }
 }
 
 bool
-PopupBoxObject::AutoPosition()
+XULPopupElement::AutoPosition()
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     return menuPopupFrame->GetAutoPosition();
   }
   return true;
 }
 
 void
-PopupBoxObject::SetAutoPosition(bool aShouldAutoPosition)
+XULPopupElement::SetAutoPosition(bool aShouldAutoPosition)
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     menuPopupFrame->SetAutoPosition(aShouldAutoPosition);
   }
 }
 
 void
-PopupBoxObject::GetPopupState(nsString& aState)
+XULPopupElement::GetState(nsString& aState)
 {
   // set this here in case there's no frame for the popup
   aState.AssignLiteral("closed");
 
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (menuPopupFrame) {
     switch (menuPopupFrame->PopupState()) {
       case ePopupShown:
         aState.AssignLiteral("open");
         break;
       case ePopupShowing:
       case ePopupPositioning:
       case ePopupOpening:
@@ -215,38 +215,38 @@ PopupBoxObject::GetPopupState(nsString& 
       default:
         NS_NOTREACHED("Bad popup state");
         break;
     }
   }
 }
 
 nsINode*
-PopupBoxObject::GetTriggerNode() const
+XULPopupElement::GetTriggerNode() const
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   return nsMenuPopupFrame::GetTriggerContent(menuPopupFrame);
 }
 
 Element*
-PopupBoxObject::GetAnchorNode() const
+XULPopupElement::GetAnchorNode() const
 {
-  nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr;
+  nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
   if (!menuPopupFrame) {
     return nullptr;
   }
 
   nsIContent* anchor = menuPopupFrame->GetAnchor();
   return anchor && anchor->IsElement() ? anchor->AsElement() : nullptr;
 }
 
 already_AddRefed<DOMRect>
-PopupBoxObject::GetOuterScreenRect()
+XULPopupElement::GetOuterScreenRect()
 {
-  RefPtr<DOMRect> rect = new DOMRect(mContent);
+  RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
 
   // Return an empty rectangle if the popup is not open.
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (!menuPopupFrame || !menuPopupFrame->IsOpen()) {
     return rect.forget();
   }
 
   nsView* view = menuPopupFrame->GetView();
@@ -258,17 +258,17 @@ PopupBoxObject::GetOuterScreenRect()
       int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel();
       rect->SetLayoutRect(LayoutDeviceIntRect::ToAppUnits(screenRect, pp));
     }
   }
   return rect.forget();
 }
 
 void
-PopupBoxObject::GetAlignmentPosition(nsString& positionStr)
+XULPopupElement::GetAlignmentPosition(nsString& positionStr)
 {
   positionStr.Truncate();
 
   // This needs to flush layout.
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true));
   if (!menuPopupFrame)
     return;
 
@@ -309,45 +309,35 @@ PopupBoxObject::GetAlignmentPosition(nsS
       break;
     default:
       // Leave as an empty string.
       break;
   }
 }
 
 int32_t
-PopupBoxObject::AlignmentOffset()
+XULPopupElement::AlignmentOffset()
 {
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (!menuPopupFrame)
     return 0;
 
   int32_t pp = mozilla::AppUnitsPerCSSPixel();
   // Note that the offset might be along either the X or Y axis, but for the
   // sake of simplicity we use a point with only the X axis set so we can
   // use ToNearestPixels().
   nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0);
   nsIntPoint popupOffset = appOffset.ToNearestPixels(pp);
   return popupOffset.x;
 }
 
 void
-PopupBoxObject::SetConstraintRect(dom::DOMRectReadOnly& aRect)
+XULPopupElement::SetConstraintRect(dom::DOMRectReadOnly& aRect)
 {
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (menuPopupFrame) {
     menuPopupFrame->SetOverrideConstraintRect(
       LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
   }
 }
 
 } // namespace dom
 } // namespace mozilla
-
-// Creation Routine ///////////////////////////////////////////////////////////////////////
-
-nsresult
-NS_NewPopupBoxObject(nsIBoxObject** aResult)
-{
-  *aResult = new mozilla::dom::PopupBoxObject();
-  NS_ADDREF(*aResult);
-  return NS_OK;
-}
rename from layout/xul/PopupBoxObject.h
rename to dom/xul/XULPopupElement.h
--- a/layout/xul/PopupBoxObject.h
+++ b/dom/xul/XULPopupElement.h
@@ -1,50 +1,72 @@
 /* -*- 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_PopupBoxObject_h
-#define mozilla_dom_PopupBoxObject_h
+#ifndef XULPopupElement_h__
+#define XULPopupElement_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "mozilla/dom/BoxObject.h"
 #include "nsString.h"
+#include "nsXULElement.h"
 
 struct JSContext;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRect;
 class Element;
 class Event;
 class StringOrOpenPopupOptions;
 
-class PopupBoxObject final : public BoxObject
+nsXULElement*
+NS_NewXULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
+
+class XULPopupElement final : public nsXULElement
 {
+private:
+  nsIFrame* GetFrame(bool aFlushLayout);
+
 public:
-  NS_INLINE_DECL_REFCOUNTING_INHERITED(PopupBoxObject, BoxObject)
+  explicit XULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+    : nsXULElement(aNodeInfo)
+  {
+  }
 
-  PopupBoxObject();
+  void GetLabel(DOMString& aValue) const
+  {
+      GetXULAttr(nsGkAtoms::label, aValue);
+  }
+  void SetLabel(const nsAString& aValue, ErrorResult& rv)
+  {
+      SetXULAttr(nsGkAtoms::label, aValue, rv);
+  }
 
-  nsIContent* GetParentObject() const;
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  void GetPosition(DOMString& aValue) const
+  {
+      GetXULAttr(nsGkAtoms::position, aValue);
+  }
+  void SetPosition(const nsAString& aValue, ErrorResult& rv)
+  {
+      SetXULAttr(nsGkAtoms::position, aValue, rv);
+  }
 
   bool AutoPosition();
 
   void SetAutoPosition(bool aShouldAutoPosition);
 
   void OpenPopup(Element* aAnchorElement,
-                 const mozilla::dom::StringOrOpenPopupOptions& aOptions,
+                 const StringOrOpenPopupOptions& aOptions,
                  int32_t aXPos,
                  int32_t aYPos,
                  bool aIsContextMenu, bool aAttributesOverride,
                  Event* aTriggerEvent);
 
   void OpenPopupAtScreen(int32_t aXPos,
                          int32_t aYPos,
                          bool aIsContextMenu,
@@ -54,17 +76,17 @@ public:
                              int32_t aXPos, int32_t aYPos,
                              int32_t aWidth, int32_t aHeight,
                              bool aIsContextMenu,
                              bool aAttributesOverride,
                              Event* aTriggerEvent);
 
   void HidePopup(bool aCancel);
 
-  void GetPopupState(nsString& aState);
+  void GetState(nsString& aState);
 
   nsINode* GetTriggerNode() const;
 
   Element* GetAnchorNode() const;
 
   already_AddRefed<DOMRect> GetOuterScreenRect();
 
   void MoveTo(int32_t aLeft, int32_t aTop);
@@ -76,18 +98,22 @@ public:
                     bool aAttributesOverride);
 
   void SizeTo(int32_t aWidth, int32_t aHeight);
 
   void GetAlignmentPosition(nsString& positionStr);
 
   int32_t AlignmentOffset();
 
-  void SetConstraintRect(dom::DOMRectReadOnly& aRect);
+  void SetConstraintRect(DOMRectReadOnly& aRect);
 
-private:
-  ~PopupBoxObject();
+protected:
+  virtual ~XULPopupElement()
+  {
+  }
+
+  JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_PopupBoxObject_h
+#endif // XULPopupElement_h
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -18,26 +18,31 @@ if CONFIG['MOZ_XUL']:
     XPIDL_SOURCES += [
         'nsIXULOverlayProvider.idl',
     ]
 
     EXPORTS += [
         'nsXULElement.h',
     ]
 
+    EXPORTS.mozilla.dom += [
+        'XULPopupElement.h',
+    ]
+
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULContentUtils.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
         'nsXULPrototypeCache.cpp',
         'nsXULPrototypeDocument.cpp',
         'nsXULSortService.cpp',
         'XULDocument.cpp',
+        'XULPopupElement.cpp',
     ]
 
 XPIDL_SOURCES += [
     'nsIController.idl',
     'nsIControllers.idl',
     'nsIXULSortService.idl',
 ]
 
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -70,16 +70,17 @@
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
 #include "nsXULTooltipListener.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsICSSDeclaration.h"
 #include "nsLayoutUtils.h"
+#include "XULPopupElement.h"
 
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/BoxObject.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/dom/XULCommandEvent.h"
 
@@ -165,19 +166,24 @@ nsXULElement::MaybeUpdatePrivateLifetime
 nsXULElement* NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
   return new nsXULElement(aNodeInfo);
 }
 
  /* static */
 nsXULElement* nsXULElement::Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
-  // Check tagname and create other types of elements here
-
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
+  if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
+      nodeInfo->Equals(nsGkAtoms::popup) ||
+      nodeInfo->Equals(nsGkAtoms::panel) ||
+      nodeInfo->Equals(nsGkAtoms::tooltip)) {
+    return NS_NewXULPopupElement(nodeInfo.forget());
+  }
+
   return NS_NewBasicXULElement(nodeInfo.forget());
 }
 
 /* static */
 already_AddRefed<nsXULElement>
 nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
                                   mozilla::dom::NodeInfo *aNodeInfo,
                                   bool aIsScriptable,
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -327,18 +327,18 @@ enum {
   XUL_ELEMENT_HAS_CONTENTMENU_LISTENER =  XUL_ELEMENT_FLAG_BIT(0),
   XUL_ELEMENT_HAS_POPUP_LISTENER =        XUL_ELEMENT_FLAG_BIT(1)
 };
 
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
 
 #undef XUL_ELEMENT_FLAG_BIT
 
-class nsXULElement final : public nsStyledElement,
-                           public nsIDOMNode
+class nsXULElement : public nsStyledElement,
+                     public nsIDOMNode
 {
 protected:
     // Use Construct to construct elements instead of this constructor.
     explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
 public:
     using Element::Blur;
     using Element::Focus;
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -26,20 +26,16 @@
 // {56E2ADA8-4631-11d4-BA11-001083023C1E}
 #define NS_SCROLLBOXOBJECT_CID \
 { 0x56e2ada8, 0x4631, 0x11d4, { 0xba, 0x11, 0x0, 0x10, 0x83, 0x2, 0x3c, 0x1e } }
 
 // {AA40253B-4C42-4056-8132-37BCD07862FD}
 #define NS_MENUBOXOBJECT_CID \
 { 0xaa40253b, 0x4c42, 0x4056, { 0x81, 0x32, 0x37, 0xbc, 0xd0, 0x78, 0x62, 0xfd } }
 
-// {6C392C62-1AB1-4de7-BFC6-ED4F9FC7749A}
-#define NS_POPUPBOXOBJECT_CID \
-{ 0x6c392c62, 0x1ab1, 0x4de7, { 0xbf, 0xc6, 0xed, 0x4f, 0x9f, 0xc7, 0x74, 0x9a } }
-
 // {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
@@ -299,17 +299,16 @@ nsresult NS_NewLayoutDebugger(nsILayoutD
 #endif
 
 nsresult NS_NewBoxObject(nsIBoxObject** aResult);
 
 #ifdef MOZ_XUL
 nsresult NS_NewListBoxObject(nsIBoxObject** aResult);
 nsresult NS_NewScrollBoxObject(nsIBoxObject** aResult);
 nsresult NS_NewMenuBoxObject(nsIBoxObject** aResult);
-nsresult NS_NewPopupBoxObject(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);
@@ -361,17 +360,16 @@ MAKE_CTOR(CreateNewLayoutDebugger,      
 #endif
 
 MAKE_CTOR(CreateNewFrameTraversal,      nsIFrameTraversal,      NS_CreateFrameTraversal)
 MAKE_CTOR(CreateNewBoxObject,           nsIBoxObject,           NS_NewBoxObject)
 
 #ifdef MOZ_XUL
 MAKE_CTOR(CreateNewListBoxObject,       nsIBoxObject,           NS_NewListBoxObject)
 MAKE_CTOR(CreateNewMenuBoxObject,       nsIBoxObject,           NS_NewMenuBoxObject)
-MAKE_CTOR(CreateNewPopupBoxObject,      nsIBoxObject,           NS_NewPopupBoxObject)
 MAKE_CTOR(CreateNewScrollBoxObject,     nsIBoxObject,           NS_NewScrollBoxObject)
 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)
@@ -504,17 +502,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_LISTBOXOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_MENUBOXOBJECT_CID);
-NS_DEFINE_NAMED_CID(NS_POPUPBOXOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_SCROLLBOXOBJECT_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);
@@ -746,17 +743,16 @@ static const mozilla::Module::CIDEntry k
 #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_LISTBOXOBJECT_CID, false, nullptr, CreateNewListBoxObject },
   { &kNS_MENUBOXOBJECT_CID, false, nullptr, CreateNewMenuBoxObject },
-  { &kNS_POPUPBOXOBJECT_CID, false, nullptr, CreateNewPopupBoxObject },
   { &kNS_SCROLLBOXOBJECT_CID, false, nullptr, CreateNewScrollBoxObject },
   { &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 },
@@ -854,17 +850,16 @@ static const mozilla::Module::CIDEntry k
 };
 
 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-listbox;1", &kNS_LISTBOXOBJECT_CID },
   { "@mozilla.org/layout/xul-boxobject-menu;1", &kNS_MENUBOXOBJECT_CID },
-  { "@mozilla.org/layout/xul-boxobject-popup;1", &kNS_POPUPBOXOBJECT_CID },
   { "@mozilla.org/layout/xul-boxobject-scrollbox;1", &kNS_SCROLLBOXOBJECT_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 },
--- a/layout/xul/moz.build
+++ b/layout/xul/moz.build
@@ -30,17 +30,16 @@ EXPORTS += [
     'nsPIListBoxObject.h',
     'nsXULPopupManager.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BoxObject.h',
     'ListBoxObject.h',
     'MenuBoxObject.h',
-    'PopupBoxObject.h',
     'ScrollBoxObject.h',
 ]
 
 UNIFIED_SOURCES += [
     'BoxObject.cpp',
     'nsBox.cpp',
     'nsBoxFrame.cpp',
     'nsBoxLayout.cpp',
@@ -77,17 +76,16 @@ if CONFIG['MOZ_XUL']:
         'nsPopupSetFrame.cpp',
         'nsProgressMeterFrame.cpp',
         'nsResizerFrame.cpp',
         'nsSplitterFrame.cpp',
         'nsTextBoxFrame.cpp',
         'nsTitleBarFrame.cpp',
         'nsXULLabelFrame.cpp',
         'nsXULPopupManager.cpp',
-        'PopupBoxObject.cpp',
         'ScrollBoxObject.cpp',
     ]
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['tree', 'grid']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -2282,17 +2282,17 @@ nsMenuPopupFrame::AttributeChanged(int32
 }
 
 void
 nsMenuPopupFrame::MoveToAttributePosition()
 {
   // Move the widget around when the user sets the |left| and |top| attributes.
   // Note that this is not the best way to move the widget, as it results in lots
   // of FE notifications and is likely to be slow as molasses. Use |moveTo| on
-  // PopupBoxObject if possible.
+  // the element if possible.
   nsAutoString left, top;
   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
   nsresult err1, err2;
   mozilla::CSSIntPoint pos(left.ToInteger(&err1), top.ToInteger(&err2));
 
   if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2))
     MoveTo(pos, false);
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -439,17 +439,17 @@ public:
    * selected. If aAsynchronous is true, the event will be dispatched
    * asynchronously. This should be true when called from frame code.
    */
   void ShowMenu(nsIContent *aMenu, bool aSelectFirstItem, bool aAsynchronous);
 
   /**
    * Open a popup, either anchored or unanchored. If aSelectFirstItem is
    * true, then the first item in the menu is selected. The arguments are
-   * similar to those for nsIPopupBoxObject::OpenPopup.
+   * similar to those for XULPopupElement::OpenPopup.
    *
    * aTriggerEvent should be the event that triggered the event. This is used
    * to determine the coordinates and trigger node for the popup. This may be
    * null if the popup was not triggered by an event.
    *
    * This fires the popupshowing event synchronously.
    */
   void ShowPopup(nsIContent* aPopup,
--- a/toolkit/content/tests/widgets/test_popupanchor.xul
+++ b/toolkit/content/tests/widgets/test_popupanchor.xul
@@ -146,32 +146,36 @@ var tests = [
       var origPanelRect = panel.getBoundingClientRect();
       panel.sizeTo(100, 100);
       isArrowPositionedOn("bottom"); // should not have flipped
       panel.sizeTo(origPanelRect.width, origPanelRect.height);
       isArrowPositionedOn("bottom"); // should not have flipped
       next();
     });
   }],
-
   ['flippingResizeHorizontal', 'middle', function(next) {
     openPopup("after_end", function() {
       isArrowPositionedOn("right");
-      panel.sizeTo(anchor.getBoundingClientRect().left + 50, 50);
-      isArrowPositionedOn("left"); // check it flipped and has zero offset.
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(anchor.getBoundingClientRect().left + 50, 50); },
+        () => {
+          isArrowPositionedOn("left"); // check it flipped and has zero offset.
+          next();
+        });
     });
   }],
-
   ['flippingResizeVertical', 'middle', function(next) {
     openPopup("start_after", function() {
       isArrowPositionedOn("bottom");
-      panel.sizeTo(50, anchor.getBoundingClientRect().top + 50);
-      isArrowPositionedOn("top"); // check it flipped and has zero offset.
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(50, anchor.getBoundingClientRect().top + 50); },
+        () => {
+          isArrowPositionedOn("top"); // check it flipped and has zero offset.
+          next();
+        });
     });
   }],
 
   ['simpleMoveToAnchorHorizontal', 'middle', function(next) {
     openPopup("after_end", function() {
       isArrowPositionedOn("right");
       waitForPopupPositioned(
        () => { panel.moveToAnchor(anchor, "after_end", 20, 0); },
@@ -266,69 +270,81 @@ var tests = [
   }],
 
   ['veryWidePanel-after_end', 'middle', function(next) {
     openSlidingPopup("after_end", function() {
       var origArrowRect = arrow.getBoundingClientRect();
       // Now move it such that the arrow can't be at either end of the panel but
       // instead somewhere in the middle as that is the only way things fit,
       // meaning the arrow should "slide" down the panel.
-      panel.sizeTo(window.innerWidth - 10, 60);
-      is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested.")
-      // the arrow should not have moved.
-      var curArrowRect = arrow.getBoundingClientRect();
-      is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
-      is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(window.innerWidth - 10, 60); },
+        () => {
+          is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested.")
+          // the arrow should not have moved.
+          var curArrowRect = arrow.getBoundingClientRect();
+          is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
+          is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
+          next();
+      });
     });
   }],
 
   ['veryWidePanel-before_start', 'middle', function(next) {
     openSlidingPopup("before_start", function() {
       var origArrowRect = arrow.getBoundingClientRect();
       // Now size it such that the arrow can't be at either end of the panel but
       // instead somewhere in the middle as that is the only way things fit.
-      panel.sizeTo(window.innerWidth - 10, 60);
-      is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested")
-      // the arrow should not have moved.
-      var curArrowRect = arrow.getBoundingClientRect();
-      is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
-      is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(window.innerWidth - 10, 60); },
+        () => {
+          is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested")
+          // the arrow should not have moved.
+          var curArrowRect = arrow.getBoundingClientRect();
+          is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
+          is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
+          next();
+      });
     });
   }],
 
   ['veryTallPanel-start_after', 'middle', function(next) {
     openSlidingPopup("start_after", function() {
       var origArrowRect = arrow.getBoundingClientRect();
       // Now move it such that the arrow can't be at either end of the panel but
       // instead somewhere in the middle as that is the only way things fit,
       // meaning the arrow should "slide" down the panel.
-      panel.sizeTo(100, window.innerHeight - 10);
-      is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested.")
-      // the arrow should not have moved.
-      var curArrowRect = arrow.getBoundingClientRect();
-      is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
-      is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(100, window.innerHeight - 10); },
+        () => {
+          is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested.")
+          // the arrow should not have moved.
+          var curArrowRect = arrow.getBoundingClientRect();
+          is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
+          is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
+          next();
+        });
     });
   }],
 
   ['veryTallPanel-start_before', 'middle', function(next) {
     openSlidingPopup("start_before", function() {
       var origArrowRect = arrow.getBoundingClientRect();
       // Now size it such that the arrow can't be at either end of the panel but
       // instead somewhere in the middle as that is the only way things fit.
-      panel.sizeTo(100, window.innerHeight - 10);
-      is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested")
-      // the arrow should not have moved.
-      var curArrowRect = arrow.getBoundingClientRect();
-      is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
-      is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
-      next();
+      waitForPopupPositioned(
+        () => { panel.sizeTo(100, window.innerHeight - 10); },
+        () => {
+          is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested")
+          // the arrow should not have moved.
+          var curArrowRect = arrow.getBoundingClientRect();
+          is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
+          is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
+          next();
+      });
     });
   }],
 
   // Tests against the anchor at the right-hand side of the window
   ['afterend', 'right', function(next) {
     openPopup("after_end", function() {
       // when we request too far to the right/bottom, the panel gets shrunk
       // and moved.  The amount it is shrunk by is how far it is moved.
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -10,207 +10,16 @@
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="popup-base">
     <resources>
       <stylesheet src="chrome://global/skin/popup.css"/>
     </resources>
-
-    <implementation>
-      <property name="label" onget="return this.getAttribute('label');"
-                             onset="this.setAttribute('label', val); return val;"/>
-      <property name="position" onget="return this.getAttribute('position');"
-                                onset="this.setAttribute('position', val); return val;"/>
-      <property name="popupBoxObject">
-        <getter>
-          return this.boxObject;
-        </getter>
-      </property>
-
-      <property name="state" readonly="true"
-                onget="return this.popupBoxObject.popupState"/>
-
-      <property name="triggerNode" readonly="true"
-                onget="return this.popupBoxObject.triggerNode"/>
-
-      <property name="anchorNode" readonly="true"
-                onget="return this.popupBoxObject.anchorNode"/>
-
-      <method name="openPopup">
-        <parameter name="aAnchorElement"/>
-        <parameter name="aPosition"/>
-        <parameter name="aX"/>
-        <parameter name="aY"/>
-        <parameter name="aIsContextMenu"/>
-        <parameter name="aAttributesOverride"/>
-        <parameter name="aTriggerEvent"/>
-        <body>
-        <![CDATA[
-          // Allow for passing an options object as the second argument.
-          if (arguments.length == 2 &&
-              arguments[1] != null &&
-              typeof arguments[1] == "object") {
-            let params = arguments[1];
-            aPosition = params.position;
-            aX = params.x;
-            aY = params.y;
-            aIsContextMenu = params.isContextMenu;
-            aAttributesOverride = params.attributesOverride;
-            aTriggerEvent = params.triggerEvent;
-          }
-
-          try {
-            var popupBox = this.popupBoxObject;
-            if (popupBox)
-              popupBox.openPopup(aAnchorElement, aPosition, aX, aY,
-                                 aIsContextMenu, aAttributesOverride, aTriggerEvent);
-          } catch (e) {}
-        ]]>
-        </body>
-      </method>
-
-      <method name="openPopupAtScreen">
-        <parameter name="aX"/>
-        <parameter name="aY"/>
-        <parameter name="aIsContextMenu"/>
-        <parameter name="aTriggerEvent"/>
-        <body>
-        <![CDATA[
-          try {
-            var popupBox = this.popupBoxObject;
-            if (popupBox)
-              popupBox.openPopupAtScreen(aX, aY, aIsContextMenu, aTriggerEvent);
-          } catch (e) {}
-        ]]>
-        </body>
-      </method>
-
-      <method name="openPopupAtScreenRect">
-        <parameter name="aPosition"/>
-        <parameter name="aX"/>
-        <parameter name="aY"/>
-        <parameter name="aWidth"/>
-        <parameter name="aHeight"/>
-        <parameter name="aIsContextMenu"/>
-        <parameter name="aAttributesOverride"/>
-        <parameter name="aTriggerEvent"/>
-        <body>
-        <![CDATA[
-          try {
-            var popupBox = this.popupBoxObject;
-            if (popupBox)
-              popupBox.openPopupAtScreenRect(aPosition, aX, aY, aWidth, aHeight,
-                                             aIsContextMenu, aAttributesOverride, aTriggerEvent);
-          } catch (e) {}
-        ]]>
-        </body>
-      </method>
-
-      <method name="hidePopup">
-        <parameter name="cancel"/>
-        <body>
-        <![CDATA[
-          var popupBox = null;
-          var menuBox = null;
-          try {
-            popupBox = this.popupBoxObject;
-          } catch (e) {}
-          try {
-            menuBox = this.parentNode.boxObject;
-          } catch (e) {}
-          if (menuBox instanceof MenuBoxObject)
-            menuBox.openMenu(false);
-          else if (popupBox instanceof PopupBoxObject)
-            popupBox.hidePopup(cancel);
-        ]]>
-        </body>
-      </method>
-
-      <property name="autoPosition">
-        <getter>
-        <![CDATA[
-          return this.popupBoxObject.autoPosition;
-        ]]>
-        </getter>
-        <setter>
-        <![CDATA[
-          return this.popupBoxObject.autoPosition = val;
-        ]]>
-        </setter>
-      </property>
-
-      <property name="alignmentPosition" readonly="true">
-        <getter>
-        <![CDATA[
-          return this.popupBoxObject.alignmentPosition;
-        ]]>
-        </getter>
-      </property>
-
-      <property name="alignmentOffset" readonly="true">
-        <getter>
-        <![CDATA[
-          return this.popupBoxObject.alignmentOffset;
-        ]]>
-        </getter>
-      </property>
-
-      <method name="moveTo">
-        <parameter name="aLeft"/>
-        <parameter name="aTop"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.moveTo(aLeft, aTop);
-        ]]>
-        </body>
-      </method>
-
-      <method name="moveToAnchor">
-        <parameter name="aAnchorElement"/>
-        <parameter name="aPosition"/>
-        <parameter name="aX"/>
-        <parameter name="aY"/>
-        <parameter name="aAttributesOverride"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
-        ]]>
-        </body>
-      </method>
-
-      <method name="sizeTo">
-        <parameter name="aWidth"/>
-        <parameter name="aHeight"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.sizeTo(aWidth, aHeight);
-        ]]>
-        </body>
-      </method>
-
-      <method name="getOuterScreenRect">
-        <body>
-        <![CDATA[
-          return this.popupBoxObject.getOuterScreenRect();
-        ]]>
-        </body>
-      </method>
-
-      <method name="setConstraintRect">
-        <parameter name="aRect"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.setConstraintRect(aRect);
-        ]]>
-        </body>
-      </method>
-    </implementation>
-
   </binding>
 
   <binding id="popup"
            extends="chrome://global/content/bindings/popup.xml#popup-base">
 
     <content>
       <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
                           smoothscroll="false">
@@ -338,40 +147,16 @@
         </xul:box>
         <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
           <children/>
         </xul:box>
       </xul:vbox>
     </content>
     <implementation>
       <field name="_fadeTimer">null</field>
-      <method name="sizeTo">
-        <parameter name="aWidth"/>
-        <parameter name="aHeight"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.sizeTo(aWidth, aHeight);
-          if (this.state == "open") {
-            this.adjustArrowPosition();
-          }
-        ]]>
-        </body>
-      </method>
-      <method name="moveToAnchor">
-        <parameter name="aAnchorElement"/>
-        <parameter name="aPosition"/>
-        <parameter name="aX"/>
-        <parameter name="aY"/>
-        <parameter name="aAttributesOverride"/>
-        <body>
-        <![CDATA[
-          this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
-        ]]>
-        </body>
-      </method>
       <method name="adjustArrowPosition">
         <body>
         <![CDATA[
         var anchor = this.anchorNode;
         if (!anchor) {
           return;
         }
 
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -210,17 +210,17 @@ Notification.prototype = {
  *        }
  */
 function PopupNotifications(tabbrowser, panel,
                                                       iconBox, options = {}) {
   if (!tabbrowser)
     throw "Invalid tabbrowser";
   if (iconBox && ChromeUtils.getClassName(iconBox) != "XULElement")
     throw "Invalid iconBox";
-  if (ChromeUtils.getClassName(panel) != "XULElement")
+  if (ChromeUtils.getClassName(panel) != "XULPopupElement")
     throw "Invalid panel";
 
   this._shouldSuppress = options.shouldSuppress || (() => false);
   this._suppress = this._shouldSuppress();
 
   this.window = tabbrowser.ownerGlobal;
   this.panel = panel;
   this.tabbrowser = tabbrowser;